Linux进程间通信

什么是进程间通信?

  • 进程间通信(简称IPC,是指在不同进程之间传播或交换信息

进程间通信有哪些方式?

  • IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享内存、Socket、Streams等。其中Socket和Streams支持不同主机上两个进程IPC

管道(通常指无名管道,是UNIX系统IPC最古老的形式)

  • 特点

    • 头文件#include <unistd.h>
    • 它是半双工的,具有固定的读端和写端
    • 他只能用于具有“亲缘关系”的进程间的通信(父子或兄弟进程之间)
    • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存当中
    • 当管道中的某个数据被读走后管道就没有此数据了
  • 原型:int pipe(int fd[2]);

    • 返回值:0=成功,-1=失败
    • 参数:fd[0]从管道读数据,fd[1]往管道写数据
    • 关闭管道:close(fd[0]),close(fd[1])
  • 用法:写的时候关闭读,读的时候关闭写

    • 读:close(fd[1]); pipe(fd[0])
    • 写:close(fd[0]); pipe(fd[1])
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    int main ()
    {
        //无名管道,一旦确定父进程为读程序运行起来后就不能将父进程改为写
        int fd[2];
        int status=0;
        int num;
        char *writebuf="Hellow World!";
        char readbuf[100]={0};
        if(pipe(fd) == -1){//先开辟管道再fork
            printf("creat pipe faild!\n");
        }
        num = fork();
        if(num < -1){  
            printf("making course file\n");
        }
        else if(num == 0){ 
            close(fd[0]);
            write(fd[1],writebuf,strlen(writebuf));
        }
        else if(num > 0){
           close(fd[1]);
           read(fd[0],readbuf,strlen(writebuf));
           printf("read:%s\n",readbuf);
        }
        else{
            printf("Nothing doing\n"); 
        }
            return 0;
    }
    

FIFO(称为命名管道,它是一种文件类型)

  • 特点

    • FIFO可以在无关的进程之间交换数据,与无名管道不同
    • FIFO有路径与之相关联,它是一种特殊设备形式存在于文件系统中
  • 原型:int mkfifo(const char *pathname, mode_t mode);

    • 返回值:0=成功,-1=失败(失败原因返回erron中)
    • mode和open函数中的mode相同,一旦创建了FIFO,就可以用一般文件I/O函数操作它。
  • 使用案例

    • read
     #include <stdio.h>
     #include <errno.h>
     #include <sys/types.h>
     #include <sys/stat.h>
     #include <unistd.h>
     #include <string.h>
     #include <fcntl.h>
     //int mkfifo(const char *pathname, mode_t mode);
     int main()
     {
         char readbuf[50]={0};
         if(mkfifo("./File",0600) == -1 && (errno == EEXIST)){
             perror("Why");
         }
         int fp = open("./File",O_RDONLY);
         for(int i=0;i<5;i++){
             read(fp,readbuf,50);
             printf("%s\n",readbuf);
         }
         close(fp);
         return 0;
     }
    
    • write
    #include <stdio.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    //int mkfifo(const char *pathname, mode_t mode);
    int main()
    {
        int fp = open("./File",O_WRONLY);//只写权限
        char *buf="Hello world!";
        for(int i=0;i<5;i++){
            sleep(1);
            write(fp,buf,strlen(buf));   
        }
        close(fp);
        return 0;
    }
    
    • 补充
      • 没有指定O_NONBLOCK(默认未指定),只读open要阻塞到某个其他进程为写而打开FIFO(就像是需要有进程对FIFO进行写,才能使得读FIFO为非阻塞)
      • 指定O_NONBLOCK,只读open立即返回。而只写open将出现错误返回-1如果没有进程已经为读而打开该FIFO,其errno置ENXIO(这种一般不用)

消息队列

  • 消息队列,是消息的链接表。一个消息队列由一个标识符(队列ID)来标识

  • 特点

    • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
    • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会删除
    • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取
  • 函数原型

    • int msgget(key_t key,int flag)

      参数说明
      key两种情况创建一个新队列:1.如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位 2.key参数为IPC_PRIVATE
      flag打开队列的方式(1.只使用IPC_CREAT:如果消息队列不存在则创建 2.IPC_CREAT和IPC_EXCL两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回)。IPC_EXCL不单独使用
      返回值成功返回队列ID,失败返回-1
    • int msgsend(int msqid,const void *prt,size_t size,int flag)

      参数说明
      msqid队列ID
      *prt发送的消息
      size消息的大小
      flag默认为0
      返回值成功返回0,失败返回-1
    • int msgrcv(int msqid,const void *prt,size_t size,long type ,int flag)

      参数说明
      msqid队列ID
      *prt接收的消息
      size消息的大小
      type消息类型(写与读要对应上)
      flag默认为0
      返回值成功返回0,失败返回-1
  • 使用案例(先运行read(会阻塞),再运行write)

    • write
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.h>
    //int msgget(key_t key, int msgflg);
    //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    struct msgbuf 
    {
       long mtype;          /* message type, must be > 0 */
       char mtext[1024];    /* message data */
    };
    int main()
    {
        key_t key;
        key = ftok(".",'z');//第一个"."参数是表示当前目录,'z'表示一个整数(1-255)
        struct msgbuf sendbuf = {888,"Hello world"};
        int ID = msgget(key,IPC_CREAT|0777);
    
        if(ID == -1){
            printf("Making queue Fiald\n");
        }
        msgsnd(ID,&sendbuf,strlen(sendbuf.mtext),0);
        if(msgctl(ID,IPC_RMID,NULL) == 0){
            printf("Delet queue success\n");
        }
        return 0;
    }
    
    • read
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    //int msgget(key_t key, int msgflg);
    //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    struct msgbuf 
    {
       long mtype;          /* message type, must be > 0 */
       char mtext[1024];    /* message data */
    };
    int main()
    {
        struct msgbuf readbuf;
        key_t key;
        key = ftok(".",'z');//第一个"."参数是表示当前目录,'z'表示一个整数(1-255)
        printf("key=%d\n",key);
        int ID = msgget(key,IPC_CREAT|0777);
    
        if(ID == -1){
            printf("Making queue Fiald\n");
        }
        msgrcv(ID,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("%s\n",readbuf.mtext);
        if(msgctl(ID,IPC_RMID,NULL) == 0){
            printf("Delet queue success\n");
        }
        return 0;
    }
    

共享内存

  • 特点

    • 共享内存是最快的一种IPC,因为进程是直接对内存进行存取
    • 因为多个进程可以同时操作,所以需要进行同步
    • 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问
  • 原型

    • int shmget(key_t key ,szie_t size, int flag);作用:创建共享内存
    参数说明
    key键值,可以由ftok()获得
    size共享内存大小(以M进行对齐)
    flag1.使用IPC_CREAT|权限:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。2.IPC_CREAT和IPC_EXCL两个同时使用:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。IPC_EXCL不单独使用
    返回值成功:返回共享内存ID 失败:返回-1
    • void *shmat(int shm_id,const void *addr,int flag);作用:挂接共享内存
    参数说明
    shm_id由shmget()返回的ID
    void *addr一般写0,让内核自己决定一个合适的地址位置
    flag一般写0,如果指定为SHM_RDONLY则以只读方式连接此段,其他值为读写方式连接此段。
    返回值成功:返回指向共享内存的地址;失败:返回-1
    • int shmdt(void *addr)作用:断开与共享内存的连接
    参数说明
    void *addr由shmat()返回的地址
    返回值成功返回0,失败返回-1
    • int shmctl(int shm_id,int cmd,struct shmid_ds *buf)作用:销毁共享内存
    参数说明
    shm_idshmget()返回的ID
    cmd设置为IPC_RMID时表示可以删除共享内存
    *buf一般为0
    返回值成功返回0,失败返回-1
    • 补充

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

        • key的值由pathname和proj_id共同决定,如果pathname和proj_id的值相同,就算在不同的程序执行所得到的key值也是一样的

        • ftok.c程序中:ftok(“/home/Desktop”,1);

        • sharer.c程序中:ftok(“/home/Desktop”,1);

        • 执行所得到的key值相同

        • pathname可以指定到目录也可以指定到特定的文件,但是路径一定要正确

        • proj_id的取值为1-255

      • key值相同时由shmget返回的ID值也相同

        • 但是由shmget创建共享内存时,当key相同下只能创建一种内存大小的内存,如果再创建相同key值下不同内存大小的共享内存,会出错
      • 查看系统中有哪些共享内存:ipcs -m

      • 删除共享内存:ipcrm -m ID

  • 使用案例(先写再读)

    • write
    #include<stdio.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <string.h>
    int main()
    {
        int ID;
        char *maddr=NULL;
        key_t key;
        key = ftok(".",1);
        ID = shmget(key,1024*2,IPC_CREAT|0666);
        if(ID == -1){
            printf("faild\n");
        }
        else{
            maddr = shmat(ID,0,0);
            strcpy(maddr,"123");
            printf("ID = %d\n" ,ID);
            printf("key = %d\n",key);
        }
        return 0;
    }
    
    • read
    #include<stdio.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <string.h>
    int main()
    {
        int ID;
        char *maddr=NULL;
        key_t key;
        key = ftok(".",1);
        ID = shmget(key,1024*2,0);
        if(ID == -1){
            perror("Faild");
        }
        else{
            maddr = shmat(ID,0,0);
            printf("read:%s\n",maddr);
            printf("ID = %d\n",key);
        }
        shmdt(maddr);
        shmctl(ID,IPC_RMID,0);
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Trt_ToHoldOn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值