Chinaunix首页 | 论坛 | 博客
  • 博客访问: 464103
  • 博文数量: 68
  • 博客积分: 2606
  • 博客等级: 上尉
  • 技术积分: 1308
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-13 23:01
文章分类
文章存档

2012年(6)

2011年(62)

分类: LINUX

2011-06-04 15:12:48

在两个进程间共享数据的方法,至今为止我们只说过利用管道。管道只是利用了输入输出重定向的原理,非常简单。而且只能在父子进程间使用。很多场合下这种方法是无法满足我们的要求的。
那么现在,我们又有了一种新的方法——利用共享内存(shared memory)。这可以使我们在任意两个进程间传递数据,而且也是相对简单容易实现的一个方法。
注意:在正常情况下,一个进程的所使用的内存区是不允许其它进程访问的。这里我们要开辟的共享内存是例外。
我们来做一个简单的剪贴板。从标准输入向mcut输入数据,mcut将其放在共享内存中,然后mpaste从共享内存中读出数据并显示。

/*      mcut.c  */
#include 
#include 
#include 
#include 

int     main()
{
        key_t   shmkey;
        int     shmid , in_tmp ;
        char    *head , *pos ,
                in_data[4096] , *in_ptr ;

        shmkey = ftok( "mcut" , 'a' );          //      计算标识符
        //      shmid开辟共享内存
        shmid = shmget( shmkey , sizeof(in_data) , IPC_CREAT | 0666 ) ;

        head = pos = shmat( shmid , 0 , 0 );    //      允许本进程使用这块共享内存

        in_ptr = in_data ;
        //      开始从标准输入输入数据,暂时存在in_data里。
        while( (in_tmp=getchar()) != EOF )
        {
                *in_ptr = in_tmp ;
                in_ptr++ ;
        }
        
        *in_ptr = '\0' ;
        in_ptr = in_data ;

        //      开始写入共享内存
        while( *in_ptr != '\0' )
        {
                *pos = *in_ptr ;
                pos++;
                in_ptr++;
        }
        *pos = '\0' ;
        shmdt( head );          //      禁止本进程使用这块内存
        return 0;
}

/*      mpaste.c        */
#include 
#include 
#include 
#include 

int     main()
{
        key_t   shmkey;
        int     shmid;
        char    *head , *pos ,
                out_data[4096] , *out_ptr ;
        
        shmkey = ftok( "mcut" , 'a' );          //      计算标识符
        //      开辟共享内存
       // shmid = shmget( shmkey , sizeof(out_data) , IPC_ALLOC | 0666 );
          shmid = shmget( shmkey , sizeof(out_data) , IPC_CREAT | 0666 );
        head = pos = shmat( shmid , 0 , 0 );    //      允许本进程使用这块共享内存
        out_ptr = out_data ;
        
        //      从共享内存中取得数据
        while( *pos != '\0' )
        {
                *out_ptr = *pos ;
                out_ptr++ ;
                pos++ ;
        }
        *out_ptr = '\0' ;
        printf( "%s\n" , out_data );
        fflush( stdout );
        shmdt( head );          //      禁止本进程使用这块共享内存
        return 0;
}

如何?明白多少了?
要使用共享内存,应该有如下步骤:
1.开辟一块共享内存      shmget()
2.允许本进程使用共某块共享内存  shmat()
3.写入/读出
4.禁止本进程使用这块共享内存    shmdt()
5.删除这块共享内存      shmctl()或者命令行下ipcrm

上面的程序中用到了如下函数,我们一一解释一下。

首先是ftok()。它有两个参数,一个是字符串,一个是字符。字符串一般用当前进程的程序名,字符一般用来标记这个标识符所标识的共享内存是这个进程所开辟的第几个共享内存。ftok()会返回一个key_t型的值,也就是计算出来的标识符的值。

其次,操作共享内存,我们用到了下面的函数
#include 
#include 
#include 

int     shmget( key_t shmkey , int shmsiz , int flag );
void    *shmat( int shmid , char *shmaddr , int shmflag );
int     shmdt( char *shmaddr );

shmget()是用来开辟/指向一块共享内存的函数。参数定义如下:
key_t shmkey 是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。但是刚才我们的两个进程没有任何关系,所以就用ftok()算出来一个标识符使用了。
int shmsiz 是这块内存的大小.
int flag 是这块内存的模式(mode)以及权限标识(关于权限的意思,请参阅本系列第五章)。
模式可取如下值:        新建:IPC_CREAT
                        使用已开辟的内存:IPC_ALLOC //貌似不存在这样的选项
                        如果标识符以存在,则返回错误值:IPC_EXCL
然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。
如:    IPC_CREAT | IPC_EXCL | 0666
这个函数成功时返回共享内存的ID,失败时返回-1。


shmat()是用来允许本进程访问一块共享内存的函数。
int shmid是那块共享内存的ID。
char *shmaddr是共享内存的起始地址
int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式
成功时,这个函数返回共享内存的起始地址。失败时返回-1。

shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。
参数char *shmaddr是那块共享内存的起始地址。
成功时返回0。失败时返回-1。

此外,还有一个用来控制共享内存的shmctl()函数如下:
#include 
#include 
#include 

int     shmctl( int shmid , int cmd , struct shmid_ds *buf );
int shmid是共享内存的ID。
int cmd是控制命令,可取值如下:
        IPC_STAT        得到共享内存的状态
        IPC_SET         改变共享内存的状态
        IPC_RMID        删除共享内存
struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
返回值:        成功:0
                失败:-1

刚才我们的mpaste.c程序中还可以加入这样几句。

struct shmid_ds buf;
... ...
shmctl( shmid , IPC_STAT , &buf );      //      取得共享内存的状态
... ... 
shmctl( shmid , IPC_RMID , &buf );      //      删除共享内存

注意!!!!!!!!!:在使用共享内存,结束程序退出后。如果你没在程序中用shmctl()删除共享内存的话,一定要在命令行下用ipcrm命令删除这块共享内存。你要是不管的话,它就一直在那儿放着了。
简单解释一下ipcs命令和ipcrm命令。

取得ipc信息:
ipcs [-m|-q|-s]
-m      输出有关共享内存(shared memory)的信息
-q      输出有关信息队列(message queue)的信息
-s      输出有关“遮断器”(semaphore)的信息
%ipcs -m

删除ipc
ipcrm -m|-q|-s shm_id
%ipcrm -m 105
阅读(1217) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~