• Linux-IPC进程间通信(day11)


    一、IPC进程间通讯(system v IPC)

    二、网络基础知识


    一、IPC进程间通讯(system v IPC)

    包含三个方面:

    1、消息队列

    2、共享内存

    3、信号量集

     可以使用ipcs查看system v IPC的对象

    IPC的操作主要有以下几个步骤:

    1、获取一个键值

      ftok(3)

      #include<sys/types.h>

      #include<sys/ipc.h>

      key_t ftok(const char *pathname,int proj_id);

      功能:

        转换pathname和proj_id为一个键值

      参数:

        pathname:指定文件名字

        proj_id:整数,不能为0

      返回值:

        成功:key值返回

        错误:-1,errno被设置

    #include<sys/types.h>
    
    #include<sys/ipc.h>
    #inlcude<stdio.h>
    
    int main(void){
        key_t key;
        key=ftok("hello",31);
        if(key==-1){
            perror("ftok");
            return 1;
        }
        printf("key=%d
    ",key);
        return 0;
    }

    键值是由文件名和proj_id决定的,且是唯一不变的。

    2、通过键值获取一块内存,并返回这块内存的ID(IPC的ID,即标识符)

    (1)消息队列

    msgget(2)(获取内存ID)

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    int msgget(key_t key,int magflg);

    功能:

      获取一个和key关联的消息队列的ID,(其中key是通过ftok获取的)

    参数:

      key

        0(IPC_PRIVATE):会建立新的消息队列

        大于0的32位整数:视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值

      msgflg

        0:取消息队列标识符,若不存在则函数会报错

        IPC_CREAT:当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符

        IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错

    返回值:

       失败:-1,errno被设置

       成功:消息队列的ID

    错误代码

    EACCES:指定的消息队列已存在,但调用进程没有权限访问它

    EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志

    ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志

    ENOMEM:需要建立消息队列,但内存不足

    ENOSPC:需要建立消息队列,但已达到系统的限制

     例:

    #include<sys/types.h>
    
    #include<sys/ipc.h>
    #inlcude<stdio.h>
    
    int main(void){
        key_t key;
        key=ftok("hello",31);
        if(key==-1){
            perror("ftok");
            return 1;
        }
        printf("key=%d
    ",key);
        //通过键值获取消息队列的ID
        int msqid =msgget(key,IPC_CREAT);
        if(msqid==-1){
            perror("msgget");
            return 2;
        }
        printf("msgqid%d
    ",msqid);
        return 0;
    }        

    此时可以使用ipcs查看IPC相关信息,看到key值可ID已经绑定。

    消息队列
    1 获取键值  ftok(3)
    2 获取消息队列的id     msgget(2)
    3 向消息队列发送消息   msgsnd(2)
    4 从消息队列获取消息    msgrcv(2)

    msgsnd(2) (将消息写入到消息队列)

    所需头文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    函数原型

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

    功能:

    将msgp消息写入到标识符为msqid的消息队列

    参数:

      msqid:要操作的消息队列,函数传入值

      msgp:消息队列标识符,发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下:

         struct s_msg{ /*msgp定义的参照格式*/
            long type; /* 必须大于0,消息类型 */
                 char mtext[256]; /*消息正文,可以是其他任何类型*/
            } msgp;

      msgsz

        要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度

      msgflg

        0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列

        IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回

        IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。

    函数返回值

      成功:0

      出错:-1,错误原因存于error中

    错误代码

      EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满

      EIDRM:标识符为msqid的消息队列已被删除

      EACCESS:无权限写入消息队列

      EFAULT:参数msgp指向无效的内存地址

      EINTR:队列已满而处于等待情况下被信号中断

      EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0

    msgrcv (从消息队列读取消息)

    所需头文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    功能:

      从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除

    函数原型

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

    参数:

    msqid

      消息队列标识符,同msgsnd

    msgp

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

    msgsz

      要接收消息的大小,不含消息类型占用的4个字节

    msgtyp

      0:接收第一个消息

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

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

    msgflg

      0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待

      IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG

      IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息

      IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃

    函数返回值

      成功:实际读取到的消息数据长度

      出错:-1,错误原因存于error中

    错误代码

      E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR

      EIDRM:标识符为msqid的消息队列已被删除

      EACCESS:无权限读取该消息队列

      EFAULT:参数msgp指向无效的内存地址

      ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读

      EINTR:等待读取队列内的消息情况下被信号中断

    msgrcv()解除阻塞的条件有以下三个:

      ①    消息队列中有了满足条件的消息。

      ②    msqid代表的消息队列被删除。

      ③    调用msgrcv()的进程被信号中断。

     

    进程A代码:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/stat.h>
    #include <sys/msg.h>
    #define MSG_FILE "/home/tarena/server.c"
    #define BUFFER 255
    #define PERM S_IRUSR|S_IWUSR
    struct msgtype 
    {
    long mtype;
    char buffer[BUFFER+1];
    };
    int main()
    {
    struct msgtype msg; 
        key_t key;
    int msgid;
    if((key=ftok(MSG_FILE,'a'))==-1)
        {
    perror("Creat Key Error");
            exit(1);
        }
    if((msgid=msgget(key,PERM | IPC_CREAT | IPC_EXCL))==-1)
        {
    perror("Creat Message Error ");
            exit(1);
        }
    while(1)
        {
            msgrcv(msgid, &msg, sizeof(struct msgtype), 1,0);
            printf("Server Receive:%s
    ",msg.buffer);
            msg.mtype=2;
            msgsnd(msgid,&msg,sizeof(struct msgtype),0);
        }
    return 0;
    }

    可再次使用ipcs查看相关信息,可以看到权限用户等已经被写入。

    进程B代码:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <sys/stat.h>
    #define MSG_FILE "/home/tarena/server.c"
    #define BUFFER 255
    #define PERM S_IRUSR|S_IWUSR
    struct msgtype
    {
    long mtype;
    char buffer[BUFFER+1];
    };
    int main(int argc,char **argv)
    {
    struct msgtype msg;
        key_t key;
    int msgid;
    if((key=ftok(MSG_FILE,'a'))==-1)
        {
            perror("Creat Key Error");
            exit(1);
        }
    if((msgid=msgget(key,PERM))==-1)
        {
            perror("Creat Message Error");
            exit(1);
        }
        msg.mtype=1;
        strcpy(msg.buffer, "这是客户端发出的消息内容");
        msgsnd(msgid, &msg, sizeof(struct msgtype), 0);
        memset(&msg, '', sizeof(struct msgtype));
        msgrcv(msgid, &msg, sizeof(struct msgtype),2,0);
        printf("Client receive:%s
    ",msg.buffer);
    return 0;
    }

    (2)共享内存

     和消息队列(使用msgget)一样,可以使用shmget(2)获取共享内存的ID。

    #include <sys/types.h> 

    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmget(key_t key, int size, int flag);

    void* shmat(int shmid, const void *addr, int flag); 
    int shmdt(char *shmaddr);
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    shmget(2)

    功能:

      用于开辟或指向一块共享内存,返回获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。

    参数:
      keyt key: 共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。
        如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。
      int size: 以字节为单位指定需要共享的内存容量。
      int flag: 包含9个比特的权限标志,它是这块内存的模式(mode)以及权限标识。 模式可取如下值:
    IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
    IPC_EXCL 与 IPC_CREAT结合使用,如果已创建则返回错误
      将“模式” 和“权限标识”进行或运算,做为第三个参数。如:IPC_CREAT | IPC_EXCL | 0640
    其中0640为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。函数调用成功时返回共享内存的ID,失败时返回-1。
      注:创建共享内存时,shmflg参数至少需要 IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是
    k=0xffffffff,不能使用;

    shmat(2)

    void* shmat(int shmid, const void *addr, int flag); 

    功能:

      用于将共享内存映射到进程的虚拟地址空间中。用来允许本进程访问一块共享内存的函数。第一次创建共享内存时,它不能任何进程访问,要想启用对该共享内存的访问,必须将其连接到一个进程的地址空间中,。shmat函数就是用来完成此工作的。

    参数:

      int shmid :共享内存的ID,即共享内存的标识。 

      char *shmaddr: 共享内存连接到进程中的起始地址,如果shmaddr为NULL,内核会把共享内存映射到系统选定的地址空间中;如果shmaddr不为NULL,内核会把共享内存映射到shmaddr指定的位置。
      注:一般情况下我们很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。所以一般把shmaddr设为NULL。
      int shmflag : 本进程对该内存的操作模式,可以由两个取值:SHM_RND和SHM_RDONLY。SHM_RND为读写模式,SHM_RDONLY是只读模式。需要注意的是,共享内存的读写权限由它的属主、它的访问权限和当前进程的属主共同决定。如果当shmflg & SM_RDONLY为true时,即使该共享内存的访问权限允许写操作,它也不能被写入。该参数通常会被设为0(进程有读写权限)。

    返回值: 

      函数调用成功时,返回共享内存的起始地址,失败时返回-1。

    shmdt函数

    功能:

      解除进程与进程虚拟地址空间的的映射。用于函数删除本进程对这块内存的使用。shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。
    参数:

      char *shmaddr 是那块共享内存的起始地址。shmget(2)的返回值。

    返回值:

      函数调用成功时返回0,失败时返回-1。

    给予共享内存通信代码A

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/shm.h>
    #define MSG_FILE "/home/tarena/进程A.c"
    #define BUFFER 4096
    #define PERM S_IRUSR|S_IWUSR
    int main()
    {
        key_t key;
    if((key=ftok(MSG_FILE,'a'))==-1)
        {
            perror("Creat Key Error");
            exit(1);
        }
    int shmid = shmget (key, BUFFER, PERM | IPC_CREAT | IPC_EXCL);
    if (shmid == -1)
        {
            perror ("shmget");
            exit (EXIT_FAILURE);
        }
    void* shmaddr = shmat (shmid, NULL, 0);
    if (shmaddr == (void*)-1)
        {
            perror ("shmat");
            exit (EXIT_FAILURE);
        }
        strcpy (shmaddr, "这是放入共享内存的内容!");
    if (shmdt (shmaddr) == -1)
        {
            perror ("shmdt");
            exit (EXIT_FAILURE);
        }
        sleep(10);
    if (shmctl (shmid, IPC_RMID, NULL) == -1)
        {
            perror ("shmctl");
            exit (EXIT_FAILURE);
        }
    return 0;
    }

    基于共享内存通讯进程B代码

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/shm.h>
    #define MSG_FILE "/home/tarena/进程A.c"
    #define BUFFER 4096
    #define PERM S_IRUSR|S_IWUSR
    int main(int argc,char **argv)
    {
        key_t key;
    if((key=ftok(MSG_FILE,'a'))==-1)
        {
            perror("Creat Key Error");
            exit(1);
        }
    int shmid = shmget (key, BUFFER, PERM);
    if (shmid == -1)
        {
            perror ("shmget");
            exit (EXIT_FAILURE);
        }
    void* shmaddr = shmat (shmid, NULL, 0);
    if (shmaddr == (void*)-1)
        {
            perror ("shmat");
            exit (EXIT_FAILURE);
        }
        printf ("%s
    ", (char*)shmaddr);
    if (shmdt (shmaddr) == -1)
        {
            perror ("shmdt");
            exit (EXIT_FAILURE);
        }
    return 0;
    }

    (3)信号量集

      略

    3、通过标识符操作这块内存

    共享内存操作
    shmctl函数

    功能:

       控制对这块共享内存的使用。

    参数:
      int shmid: 共享内存的ID,即共享内存标识。
      int cmd : 控制命令,表示要采取的动作,可取值如下:
    IPC_STAT 得到共享内存的状态:把shmid_ds结构中的数据设置为共享内存的当前关联值
    IPC_SET 改变共享内存的状态:把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID 删除共享内存段
      shmid_ds结构至少包含以下成员:
      struct shmid_ds {
      uid_t shm_perm.uid;
      uid_t shm_perm.gid;
      uid_t shm_perm.mode;
      }
      struct shmid_ds *buf: 一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。

    返回值:

      函数调用成功时返回0,失败时返回-1。


    使用ipcrm命令删除IPC通讯对象

     

  • 相关阅读:
    454 Authentication failed, please open smtp flag first!
    zabbix 调用的发邮件脚本
    Apache Shiro 标签方式授权
    Realm [realm.ShiroDbRealm@15408475] does not support authentication token
    简单的zabbix agent自动安装脚本
    创建IPC端口失败:拒绝访问
    如何对报表的参数控件赋值
    如何对报表的参数控件赋值
    Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
    mysql 执行计划走分区
  • 原文地址:https://www.cnblogs.com/ptfe/p/11031452.html
Copyright © 2020-2023  润新知