System V IPC包括三种进程间通信方式,即消息队列,信号量集和共享内存。



一。IPC对象



    IPC对象是进程间通信的实体,一旦被创建便一直存在于内存中,通过一个称为键值key_t的数


据结构来全局唯一标识。但是对IPC对象的访问并不是通过键值,而是通过标识符进行的。不同的键


值产生不同的IPC标识符,而不同的进程通过同一个键值打开IPC对象时产生的IPC标识符是相同的。


关于键值的产生可以使用ftok函数,原型如下:


    key_t ftok(const char* filename,int proj_id)      //include <sys/ipc.h>


        filename:指定文件,该文件必须存在且可取


        proj_id:计划代号


        成功返回key_t键值,失败返回-1              //详细说明见联机手册


使用同一个文件和相同的proj_id生成的键值是相同的,即filename+proj_id---->key_t是一个一对


一的函数。


    下一阶段,使用IPC键值打开一个IPC对象返回IPC标识符可以使用semget,shmget,msgget这三个


函数。详细说明见下文。


    系统为每一个IPC对象保存了一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,并确


定了一个IPC操作是否可以访问该IPC对象。


struct ipc_perm{ 
        key_t key;    //此IPC对象的键值 
        uid_t uid;    //此IPC对象用户ID 
        gid_t gid;    //此IPC对象组ID 
        uid_t cuid;   //此IPC对象创建进程的有效用户ID 
        gid_t cgid;   //此IPC对象创建进程的有效组ID 
        mode_t mode;  //此IPC对象的读写权限 
        ulong_t seq;  //IPC对象的序列号 
    };
struct ipc_perm{ 
        key_t key;    //此IPC对象的键值 
        uid_t uid;    //此IPC对象用户ID 
        gid_t gid;    //此IPC对象组ID 
        uid_t cuid;   //此IPC对象创建进程的有效用户ID 
        gid_t cgid;   //此IPC对象创建进程的有效组ID 
        mode_t mode;  //此IPC对象的读写权限 
        ulong_t seq;  //IPC对象的序列号 
    };

    当使用XXXget函数创建IPC对象的时候,会初始化ipc_perm结构体,将数据成员赋值。其中mode


参数会根据XXXget函数中的flag参数设置模式,若访问一个已存在的IPC对象,则flag设置为0即可。



二。消息队列(include <sys/msg.h>)



    消息队列在内核地址空间内被描述为一个链表,每个消息队列都有唯一的一个消息队列标识号。


系统为所有消息队列维护一个msgque链表,链表中的每个节点都有一个指向一个msqid_ds结构的指针,


该结构完整的描述了一个消息队列。


            time_t msg_stime;     };


    消息队列的相关函数:


    int msgget(key_t key,int flag);


        功能:创建或得到一个消息队列标识符


struct msqid_ds{ 
        struct ipc_perm msg_perm; 
        struct msg* msg_first; 
        struct msg* msg_last; 
        time_t msg_stime; 
        time_t msg_rtime; 
        time_t msg_ctime; 
        ushort msg_cbytes; 
        ushort msg_qnum; 
        ushort msg_qbytes; 
        ushort msg_lspid; 
        ushort msg_lrpid; 
    };
struct msqid_ds{ 
        struct ipc_perm msg_perm; 
        struct msg* msg_first; 
        struct msg* msg_last; 
        time_t msg_stime; 
        time_t msg_rtime; 
        time_t msg_ctime; 
        ushort msg_cbytes; 
        ushort msg_qnum; 
        ushort msg_qbytes; 
        ushort msg_lspid; 
        ushort msg_lrpid; 
    };

        key:当被指定为IPC_PRIVATE即0时,会由系统设置key创建新的消息队列


             当key大于0时,视参数flag来确定操作


        flag:IPC_CREAT:如果这个队列在内核中不存在,则创建它。


              IPC_EXCL:当与IPC_CREAT一起使用时,如果这个队列已存在,则创建失败。


        返回:成功返回IPC对象标识符,失败返回-1


        如果IPC_CREAT单独使用,semget()为一个新创建的消息队列返回标识号,或者返回具有相同


        键值的已存在队列的标识号。如果IPC_EXCL与IPC_CREAT一起使用,要么创建一个新的队列,


        要么对已存在的队列返回-1。IPC_EXCL单独是没有用的,当与IPC_CREAT结合起来使用时,可


        以保证新创建队列的打开和存取。


        同时flag为模式标志参数,使用时需要需要与存取权限进行位或(|)运算。


    实例代码如下:


        int id=msgget(IPC_PRIVATE,IPC_CREAT|0600);



    int msgctl(int msgqid,int cmd,struct msqid_ds* buff)  


        功能:用于对消息队列对象的控制,


        msqid:要操作的消息队列标识符


        cmd:要执行的操作,可取值包括:


            IPC_STAT:获得msqid的消息队列头数据到buff中


            IPC_SET:设置消息队列的属性,要设置的属性存于buff中,可设置的属性如下:


            msg_perm.uid,msg_perm.gid,msg_perm.mode以及msg_qbytes


        返回:成功返回0,失败返回-1.


    实例代码:


        msqid_id buff;


        msgctl(id,IPC_STAT,&buff);



    int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg)


        功能:将msgp消息写入标识符为msqid的消息队列


        msqid:要操作的消息队列标识符


        msgp:发送给队列的消息,可以是任何类型的结构,但第一个字段必须为long类型,标明此发


              送消息的类型,msgrcv根据此接收消息。msgp的参照格式如下:


              struct msg{


                  long type;     //大于0,消息类型


                  <Type> msg_text;//正文


              }msgp;


              <Type>是指这里可以是任意类型的结构数据


        msgsz:消息正文的大小,不包含消息类型的4字节,即sizeof(msg)-sizeof(long)


        msgflg:设定执行的详细操作,可取值如下:


            IPC_NOWAIT:当消息队列满的时候,函数不等待,直接返回


            IPC_NOERROR:发送消息大于size字节,则把消息截断。


        返回:成功返回0,失败返回-1.



    ssize_t msgrcv(int msqid,void* msgp,size_t msgsz,long msgtyp,int msgflg)


        功能:从标识符为msqid的消息队列读取消息并存于msgp,读取后的消息将从队列中删除


        msqid:要操作的消息队列标识符


        msgp: 存放消息的结构体,结构体类型要与msgsnd发送的类型相同


        msgsz:要接受消息的大小,不包含消息类型占用的4字节


        msgtyp:=0:接收第一个消息


               >0:接收类型等于msgtyp的第一个消息


               <0:接收类型小于或等于msgtyp绝对值的第一个消息


        msgflg:0:阻塞时接收消息,没有符合类型的消息则一直阻塞等待


               IPC_NOWAIT:没有可取的消息时,函数返回ENOMSG错误消息


               IPC_EXCEPT:与msgtyp配合使用,返回队列中第一个类型部位msgtyp的消息


               IPC_NOERROR:消息截断


    实例代码如下:


typedef struct msg{    
       long type; 
      char text[10]; 
    }MSG; 

    int sendmsg(const char* filename,int proj_id) 
    { 
        key_t key=ftok(filename,proj_id); 
        int id=msgget(key,IPC_CREAT|0600); 
        MSG buf; 
        buf.type=1; 
        strcpy(buf.text,"hello"); 
        int sendlength=sizeof(MSG)-sizeof(long); 
        msgsnd(id,&buf,sendlength,0); 
    } 


    int recvmsg(const char* filename,int proj_id) 
    { 
        key_t key=ftok(filename,proj_id); 
        int id=msgget(key,IPC_CREAT|0600); 
        MSG buf; 
        int recvlength=sizeof(MSG)-sizeof(long);     
        msgrcv(id,&buf,recvlength,1,0); 
        printf("%s\n",buf.text); 
    }
typedef struct msg{    
       long type; 
      char text[10]; 
    }MSG; 

    int sendmsg(const char* filename,int proj_id) 
    { 
        key_t key=ftok(filename,proj_id); 
        int id=msgget(key,IPC_CREAT|0600); 
        MSG buf; 
        buf.type=1; 
        strcpy(buf.text,"hello"); 
        int sendlength=sizeof(MSG)-sizeof(long); 
        msgsnd(id,&buf,sendlength,0); 
    } 


    int recvmsg(const char* filename,int proj_id) 
    { 
        key_t key=ftok(filename,proj_id); 
        int id=msgget(key,IPC_CREAT|0600); 
        MSG buf; 
        int recvlength=sizeof(MSG)-sizeof(long);     
        msgrcv(id,&buf,recvlength,1,0); 
        printf("%s\n",buf.text); 
    }