zoukankan      html  css  js  c++  java
  • Linux基础(14)进程通信 IPCs

    Linux进程间通信方式汇总

     目前已包含的方式
    1. 管道(PIPE) 无名管道   优: 简单,传输速度快 缺:只能用作于亲缘关系进程的通信,单向
    2. FIFO(有名管道)      优: 可以用作非亲缘关系进程的通信, 缺: 传输慢 ,不能存存储数据 ,传输量小 ,只能一对一
    3. XSI消息队列      优: 数据控制好,先来的先处理,可以做优先级,也可以用作非亲缘进程的通信 缺: 不能做大量数据处理的通信,速度慢 ,比如可以传输命令
    4. XSI信号量       不适用于进程间的通信, 可用于进程间的同步,解决生产者与消费者之间的逻辑
    5. XSI共享内存
    6. POSIX信号量
    7. 域套接字(Domain Socket)
    8. 信号(Signal)
    9. 互斥量(Mutex)
    10. sendmsg

      1.pipe(int *pipe_fd); 主进程创建管道文件,fork()后父子进程都继承了该文件索引可以指向管道文件,父子进程各关闭一个句柄,父进程往pipe_fd[1]写,子进程从pipe_fd[0]读 ,pipe虽然是一个文件, 但是是一个特殊文件存在于内存中,不具备存储功能(只具有数据流通性,不具备数据缓冲性 ,如果没读完就关闭写端,pipe则会一次性把全部数据发送给读端) ,如果父子进程各占一个的fd其中一个关闭了,那么进程会发出一个sigpipe信号另一端也将自动关闭, 管道两端 write read都会返回--1,

    #include<stdio.h>
    #include<errno.h>
    #include<sys/wait.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    
    int main()
    {
        
        int pipe_fd[2] = {-1,-1};
        
        
        if(pipe(pipe_fd) < 0){
            printf("pipe error, %s
    ",strerror(errno));
            return 1;
        }
    
        pid_t id = fork();
        
        if(id == 0){
            //child write
            close(pipe_fd[0]);
    
            char *msg = "child write:enenshiwo
    ";
            while(1){
                write(pipe_fd[1],msg,strlen(msg));
                sleep(1);
            }
            
            
        }else{
            //father read
            close(pipe_fd[1]);
    
            char buf[1024];
            while(1){
                buf[0] = '';
                 char *msg_f = "father write:enenshiwo
    ";
                ssize_t _sz = read(pipe_fd[0],buf,sizeof(buf) - 1);
                if(_sz > 0){
                    buf[_sz] = '';
                }
    
                printf("father read : %s
    ",buf);
            }
            wait(NULL);
        }
        return 0;
    }
    pipe

      2.int mkfifo(const char* pathname, mode_t mode); 依赖于文件系统, 像普通文件一样具有磁盘路径,文件权限和其他属性,所有程序都可以通过path找到有名管道 ,fifo是一个文件,数据存储在内存,当两个进程都消失,数据消息,文件只有接口的作用(当两个进程都退出时,fifo文件还存在,下次可以继续使用,但是数据不会保存)

        举个例子在shell: sudo mknod PIPETEST p 创建一个名为PIPETEST 管道文件, (在ipc里 > 代表输出到哪里 ,< 代表从哪里输入) cat xxx.c >PIPETEST 把xxx.c的内容放入到 PIPETEST 里, 再 cat<PIPETEST 把PIPETEST 的内容读到shell里

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    
    int main(int argc,char *argv[])
    {
        int pipe_fd;
        int res;
        char buffer[]="hello world!";
    
        if (access(FIFO_NAME, F_OK) == -1) //判断是否存在
        {
            res = mkfifo(FIFO_NAME, 0766);    //如果不存在则创建fifo文件
            if (res != 0) 
            {
                fprintf(stderr, "Could not create fifo %s
    ", FIFO_NAME);
                exit(EXIT_FAILURE);
            }
        }
    
        printf("Process %d opening FIFO O_WRONLY
    ", getpid());
        pipe_fd = open(FIFO_NAME, O_WRONLY);    //打开fifo文件
        printf("the file's descriptor is %d
    ", pipe_fd);
    
        if (pipe_fd != -1) 
        {
                res = write(pipe_fd, buffer, sizeof(buffer));    //往fifo里写入数据
                if (res == -1) 
                {
                    fprintf(stderr, "Write error on pipe
    ");
                    exit(EXIT_FAILURE);
                }
            printf("write data is %s,%d bytes is wirte
    ",buffer,res);
            (void)close(pipe_fd); 
        }
        else 
            exit(EXIT_FAILURE);        
        printf("Process %d finished
    ", getpid());
        exit(EXIT_SUCCESS);
    }
    fifo_write
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    
    int main(int argc,char *argv[])
    {
        int pipe_fd;
        int res;
        char buffer[4096];
        int bytes_read = 0;
    
        memset(buffer, '', sizeof(buffer));
        
        printf("Process %d opening FIFO O_RDONLY
    ", getpid());
        pipe_fd = open(FIFO_NAME, O_RDONLY);
        printf("the file's descriptor is %d
    ",pipe_fd);
    
        if (pipe_fd != -1) 
        {
                bytes_read = read(pipe_fd, buffer, sizeof(buffer));
                printf("the read data is %s
    ",buffer);
                close(pipe_fd);
        }
        else 
            exit(EXIT_FAILURE);
        printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);
        exit(EXIT_SUCCESS);
    }
    fifo_read

      3.XSI消息队列

    struct msqid_ds {
      struct ipc_perm msg_perm;     /* Ownership and permissions */  messagequeue的权限
                                             三个time主要用来监控消息队列什么时候有数据什么时候发送数据什么时候接收数据
      time_t msg_stime; /* Time of last msgsnd(2) */          最后一次的发送msg时间   time_t msg_rtime; /* Time of last msgrcv(2) */          最后一次的接收msg时间   time_t msg_ctime; /* Time of last change */            最后一次的msg改变的时间   unsigned long __msg_cbytes; /* Current number of bytes in   queue的数目 具体有多少个msqid_ds结构体 queue (nonstandard) */   msgqnum_t msg_qnum; /* Current number of messages   消息的数目 代表msqid_ds里有多少个msg in queue */   msglen_t msg_qbytes; /* Maximum number of bytes     队列中允许的最大字节数 allowed in queue */   pid_t msg_lspid; /* PID of last msgsnd(2) */    最后一个发送进程   pid_t msg_lrpid; /* PID of last msgrcv(2) */    最后一个接收进程   struct msg * msq_first ;  指向第一个消息   struct msg * msq_last ;   指向最后一个指针  这两个指针指向了消息队列 ,可以增删改查 };

    struct ipc_perm  //拥有者及权限的对象
    {
      __kernel_key_t key;
      __kernel_uid_t uid;  //用户创建者
      __kernel_gid_t gid;  //用户组
      __kernel_uid_t cuid;  //当前有效ID
      __kernel_gid_t cgid;  //当前有效组ID
      __kernel_mode_t mode;
      nsigned short seq;
    };


    可通过id索引来找到对应的共享内存或信号量或消息队列, 当我们创建时指定一个id(如果让系统随机指定,那么进程要使用时就会找不到对应id) ,
    可能会和已有id重叠,为避免id重叠所以有了key值,可通过key_t ftok(const char *pathname, int proj_id);来返回一个key值
                               key 31-24 proj_id 低8位    用proj_id作为key值的24位到31位
                               key 23-16 st_dev属性的低8位  用设备号st_dev作为key值的16位到23位
                               key 15-0 st_ino属性的低16位  用文件的inode节点号作为key值的0到15位
     

         struct msqid_ds结构体是消息队列的一部位,用来控制和指定消息队列或信号量,共享内存的权限, 而该结构体可以管理权限是因为有ipc_perm ,具体可以在Linux shell: ipcs 查看系统的共享内存机制等信息 , ipcrm [-cmd] msqid

         消息队列可以通过消息类型及优先级做出区别和控制

        消息队列的优点:  适应于量小且控制性的数据时使用消息队列

       和管道的区别, msgsnd的data会一直存在于队列里, 每次打开都可以msgrcv 消息队列里的data , 且消息队列创建后会一直存在

        1. 消息队列是两个不相关进程之间传递数据的有效渠道.

        2. 与命名管道相比,具有的优势是:
             独立于发送以及接收进程而存在;  比如fifo如果退出了接口后是无法再次获得内容的 ,而消息队列可以二次打开还是原来的内容
             消除有名管道的打开以及关闭而存在的困难;  
             可以提前查看紧急信息;
             避免命名管道的同步以及阻塞问题;
        消息队列的缺点: 发送数据量小
             与有名管道一样,每个数据块都有一个最大长度限制;
             系统中所有队列包含的全部数据块长度也有一个上限;

         消息队列: key_t ftok(const char *pathname, int proj_id); 给系统一个文件路径(就算ID相同只要pathname不相同key值就不会重叠)和指定ID , 让系统生成key值

             int msgget(key_t key, int msgflg);       用key值作为一个消息队列的id ,创建或打开一个消息队列

                       #define IPC_CREAT  00001000   /* 创建 */

                       #define IPC_EXCL   00002000   /* 如果key存在则失败 */

                       #define IPC_NOWAIT 00004000   /* return error on wait */

             如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。

             int msgctl(int msgqid, int cmd, struct msqid_ds *buf);  消息队列的控制函数, 如果是info第三参数则使用buf , 如果是rmid给NULL就可以

                       IPC_STAT :读取消息队列属性,msqid_ds里的信息

                       IPC_SET  : 设置消息队列属性 , msqid_ds里的信息

                       IPC_RMID :删除消息队列  

                       IPC_INFO :Linux特有命令,获取并保存 INFO结构体里的全部信息,保存在buf中

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

                   msgp:用户定义的缓冲区(可以自定义一个struct{type,data};) , msgsz:消息内容长度 , 

                   msgflg: , 0:消息队列满时msgsnd阻塞 , IPC_NOWAIT:队列满时msgsnd不阻塞立刻返回

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

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

    #pragma once
    
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/msg.h>
    #include<errno.h>
    #include<string.h>
    
    #define _PATH_ "."
    #define _PROJ_ID_ 0x6666
    #define _SIZE_ 1024
    
    #define CLIENT_TYPE 1
    #define SERVER_TYPE 2
    
    struct msgbuf
    {
        long mtype;
        char mtext[_SIZE_];
    };
    
    int creat_queue();
    int get_queue();
    
    int send_msg(int msgqueue_id,int who,char *msg);
    int recv_msg(int msgqueue_id,int want,char out[],int out_len);
    int delete_msgqueue(int msgqueue_id);
    
    
    static int com_creat_queue(int flags)
    {
        key_t key = ftok(_PATH_,_PROJ_ID_);
        if(key < 0){
            printf("%d:%s
    ",errno,strerror(errno));
            return -1;
        }
        int msg_id = msgget(key,flags);
        if(msg_id < 0){
            printf("%d:%s
    ",errno,strerror(errno));
            return -2;
        }
        return msg_id;
    }
    
    int creat_queue()
    {    /*创建msqidd 对象并返回操作的消息队列的ID*/
        int flags = IPC_CREAT|IPC_EXCL|0666;
        return com_creat_queue(flags);
    }
    
    int get_queue()        //获取消息队列(返回ID)用作读取等操作
    {
        int flags = IPC_CREAT;
        return com_creat_queue(flags);
    }
    
    int send_msg(int msg_id,int who,char *msg)
    {
        struct msgbuf _buf;                 //创建消息队列的类型和消息
        memset(&_buf,'',sizeof(_buf));    //清空结构体
        _buf.mtype = who;                    //赋值消息类型,可根据消息类型做出判断
        strncpy(_buf.mtext,msg,sizeof(msg)+1);    //赋值消息
        printf("say : %s
    ",msg);                //打印要发送的消息
        return msgsnd(msg_id,&_buf,sizeof(_buf.mtext),0);    //发送消息 最后一个参数0:消息队列满时msgsnd阻塞 , IPC_NOWAIT:队列满时msgsnd不阻塞立刻返回
    }
    int recv_msg(int msg_id,int want,char out[],int out_len)
    {
        struct msgbuf _buf;                        //创建消息缓冲区
        memset(&_buf,'',sizeof(_buf));        //初始化消息类型
        int ret = msgrcv(msg_id,&_buf,sizeof(_buf.mtext),want,0);    //用_buf接收消息
        if(ret <= -1)
        {
            printf("%d:%s
    ",errno,strerror(errno));
            return -1;
        }
        memset(out,'',out_len);        //初始化返回的缓冲区
        strcpy(out,_buf.mtext);            //仅返回消息类型不需要了
        return 0;
    }
    int delete_queue(int msg_id)
    {
        int ret = msgctl(msg_id,IPC_RMID,NULL);
        if(ret < 0){
            printf("%d:%s
    ",errno,strerror(errno));
            return -3;
        }
        return 0;
    }
    msgqueue.c
    #include"common.h"
    
    int server()
    {
        int msg_id = creat_queue();
        if(msg_id < 0){
            printf("%s
    ",strerror(errno));
            return -1;
        }
        char buf[_SIZE_];
        while(1)
        {
            sleep(5);
            memset(buf,'',sizeof(buf));
            int ret = recv_msg(msg_id,CLIENT_TYPE,buf,sizeof(buf));
            if(ret == 0){
                if(strncasecmp(buf,"quit",4) == 0){
                    printf("client leave!
    ");
                    break;
                }
                printf("client say : %s
    ",buf);
            }
            printf("Please Enter :");
            fflush(stdout);
            memset(buf,'',sizeof(buf));
            scanf("%s",buf);
            send_msg(msg_id,SERVER_TYPE,buf);
        }
        return delete_queue(msg_id);
    }
    int main()
    {
        server();
        return 0;
    }
    server
    #include"common.h"
    
    int client()
    {
        int msg_id = get_queue();
        char buf[_SIZE_];
        while(1)
        {
            printf("Please Enter : ");
            fflush(stdout);
            memset(buf,'',sizeof(buf));
            scanf("%s",buf);
            send_msg(msg_id,CLIENT_TYPE,buf);
            if(strncasecmp(buf,"quit",4) == 0){
                printf("client quit
    ");
                return 0;
            }
            sleep(2);
            memset(buf,'',sizeof(buf));
            int ret = recv_msg(msg_id,SERVER_TYPE,buf,sizeof(buf));
            if(ret == 0){
                printf("server say : %s
    ",buf);
            }
        }
    }
    int main()
    {
        client();
        return 0;
    }
    client

      4.XSI信号量

         可以实现进程间的同步(比如业务逻辑, 有了生产者才有消费者, 而生产得过多容量不够了,要等消费者消费到一定时才继续生产)

         可以表示系统可用资源个数(消费者可消费的个数 ,当容量的个数为0时阻塞,有一个数据生产出来时资源从0 +1,)

    struct semid_ds {
        struct ipc_perm sem_perm;  /* Ownership and permissions */
        time_t          sem_otime; /* Last semop time */
        time_t          sem_ctime; /* Last change time */
        unsigned long   sem_nsems; /* No. of semaphores in set */  //信号量的个数
        struct   sem *    sem_base                     //信号量链表的头指针
    };
    struct sem{
        int semval  信号量的值 ,代表当前信号可消耗资源的个数 ,如果是1那么当前信号可以用1次 ,如果为0当前信号不能进行消耗操作
        int sempid  最近一个操作的进程号
    }

      1.创建对象 int semget(key_t k,int n,int semflag); 创建一个sem

                key:所创建或打开信号量集的键值

                nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效

                semflg:调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过or表示  IPC_CREAT | 权限

      2.设置对象 int semctl(int sid,int semnum,int cmd,union semun a); 设置或获取sem里val的初始值

              semnum代表信号量的序号0代表第一个信号量
              semun用作保存保存或设置信号量的val(val可能是个整型也可能是指针)
              cmd不仅继承了msgctl 的cmd 还外加了一些选项

    IPC_STAT 读取并另存信号量semid_ds里的信息
    IPC_SET 设置semid_ds里的信息
    IPC_RMID 删除信号量
    IPC_INFO 获取info结构体里面的信息并保存在semun的buf里

    ctl的semnum取决于cmd的选项

    GETPID 返回操作IPC的PID  semnum为NULL semun为NULL semctl()会返回创建当前对象的pid
    GETVAL 获取信号的值    semnum为需要返回的信号的号 比如2就会返回sid里sem_base指向的链表的第二个的值 ,semun为NULL
    GETALL 获取所有信号的值  semnum为NULL semun存储所有信号值空间的首地址array 就是获得sem_base李所有val
    SETVAL 设置信号的值    semnum要设置序号 semun为设置值 100 200 等等
    SETALL 设置所有信号的值  semnum为0  semun为0 所有值设置为0

    union semun {            保存或设置的val,val可能是个整型,指针等

           int val; /* Value for SETVAL */

           struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */

           unsigned short* array; /* Array for GETALL, SETALL */

           struct seminfo *__buf; /* Buffer for IPC_INFO   (Linux-specific) */

        };

      3.使用对象 int semop(int s,struct sembuf *sb,size _t n); ,在信号量的初始值上增删(消费 || 生产)

    semop()的作用其实就是对资源(信号里的值代表了当前信号可消耗的个数)的个数加减
     s是sem_id ,sembuf是要操作的内容(加或减) n是找到sem_base对应的第n个信号量进行加减
    struct
    sembuf{ unsigned short int sem_num; //值 , 操作信号在信号集中的编号,第一个信号的编号是0。 short int sem_op;       //选项, 主要是对信号值进行加减的 ,比如5那么val+5 如果是-5那么val-5 short int sem_flg;      //权限: IPC_NOWAIT :如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作
                       //    SEM_UNDO  :进程退出后,该进程对sem进行的操作将被撤销(重新初始化信号量) };


      5. XSI共享内存  不属于任何一个进程,是一个共享空间 ,好比全局变量不需要任何一个函数,生命周期和进程相同

    内存共享的对象
    struct
    shmid_ds { struct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ __kernel_time_t shm_atime; /* last attach time */ __kernel_time_t shm_dtime; /* last detach time */ __kernel_time_t shm_ctime; /* last change time */ __kernel_ipc_pid_t shm_cpid; /* pid of creator */ __kernel_ipc_pid_t shm_lpid; /* pid of last operator */ unsigned short shm_nattch; /* no. of current attaches */ }

      共享内存重点在于互斥,所以要加锁避免两个进程同时访问当前共享内存,或使用信号量解决两个进程的同步问题

        1. int shmget(key_t key, size_t size, int shmflg);  创建共享内存

          size一般就几K共享内存不会太大 其权限shmflg:  读SHM_R , 写SHM_W

        2. int shmctl(int shmid, int cmd, struct shmid_ds *buf);  设置共享内存

    cmd:
    SHM_INFO
    struct shm_info { int    used_ids;         /* # of currently existing segments */ unsigned long shm_tot;       /* Total number of shared memory pages */ unsigned long shm_rss;      /* # of resident shared memory pages */ unsigned long shm_swp;      /* # of swapped shared memory pages */ unsigned long swap_attempts;   /* Unused since Linux 2.4 */ unsigned long swap_successes;  /* Unused since Linux 2.4 */ };

      SHM_STAT    获取
      SHM_LOCK    锁  :避免多个进程同时访问共享内存
      SHM_UNLOCK   解锁 :结束当前访问后允许其他进程访问当前共享内存

        3.void *shmat(int shmid, const void *shmaddr, int shmflg);  使用对象(使用共享内存)attach ,将一个地址指向共享内存的首地址

         shmaddr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置

         shmflgSHM_RDONLY  以只读的方式

             SHM_REMAP    以重映射的方式

             SHM_EXEC    以可执行方式

          int shmdt(const void *shmaddr);          结束使用共享内存detach

        

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <string.h>
    
    
    int main(int argc,char *argv[])
    {
        int running=1;
        int shid;
        int semid;
        int value;
        void *sharem=NULL;
    
        //初始化设置信号量对象 , 解决进程的同步(业务逻辑有生产才有消费)
        struct sembuf  sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_flg = SEM_UNDO;
        
        if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)    //获取(不存在则创建)信号量对象并获得其ID
        {
            perror("semget");
            exit(EXIT_FAILURE);
        }
        
        if (semctl(semid, 0, SETVAL, 0) == -1)                    //设置第一个信号量值为0
        {
            printf("sem init error");
            if(semctl(semid,0,IPC_RMID,0)!=0)                    //如果设置semid出错则删除所创建的sem并退出程序
            {
                perror("semctl");
                exit(EXIT_FAILURE);
            }
        exit(EXIT_FAILURE);    
        }
        shid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);    //获取或创建 shm共享内存
        if(shid==-1)
        {
            perror("shmget");
            exit(EXIT_FAILURE);
        }
    
        
        sharem=shmat(shid,NULL,0);                                //开始访问共享内存,让系统选定一个地址映射共享内存
        if(sharem==NULL)
        {
            perror("shmat");
            exit(EXIT_FAILURE);
        }
        
        while(running)
        {
            if((value=semctl( semid, 0, GETVAL ))==0)        //获取第一个信号量的值
            {    
                printf("write data operate
    ");
                printf("please input something:");
                scanf("%s",sharem);                            //往共享内存里输入数据
                sem_b.sem_op = 1;                            //设置信号量+1
                if (semop(semid, &sem_b, 1) == -1)             //产出事件,信号量的值+1
                {
                    fprintf(stderr, "semaphore_p failed
    ");
                    exit(EXIT_FAILURE);
                }
            }
            if(strcmp(sharem,"end")==0)                        //判断共享内存是否输入完成
                running--;
        }
            shmdt(sharem);                                    //断开访问共享内存
            return 0;
    }
    sem_shm_send
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/sem.h>
    
    #include "shmemory.h"
    
    int main(int argc,char *argv[])
    {
        int running=1;
        char *shm_p=NULL;
        int shmid;    
        int semid;
        int value;
        
        //初始化设置信号量对象 , 解决进程的同步(业务逻辑有生产才有消费)
        struct sembuf  sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_flg = SEM_UNDO;    
                    
        if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)        //获取(不存在则创建)信号量对象并获得其ID
        {
            perror("semget");
            exit(EXIT_FAILURE);
        }
        shmid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);    //获取(不存在则创建)共享内存对象并获得其ID
        if(shmid==-1)
        {
            perror("shmget");
            exit(EXIT_FAILURE);
        }
        shm_p=shmat(shmid,NULL,0);                                    //开始访问共享内存
        if(shm_p==NULL)
        {
            perror("shmat");
            exit(EXIT_FAILURE);
        }
        
        while(running)
        {
            if((value=semctl( semid, 0, GETVAL ))==1)                //获取第一个信号量里的值(可消耗个数),有可消耗个数才操作
            {
                printf("read data operate
    ");
                sem_b.sem_op = -1;                                    //信号量可消耗个数-1
                if (semop(semid, &sem_b, 1) == -1)                     //设置信号量的值
                {
                    fprintf(stderr, "semaphore_p failed
    ");
                    exit(EXIT_FAILURE);
                }
                printf("%s
    ",shm_p);
            }
            if(strcmp(shm_p,"end")==0)                                //判断共享内存的数据是否最后一个
                running--;
        }
        shmdt(shm_p);                                                //断开访问共享内存
        if(shmctl(shmid,IPC_RMID,0)!=0)                                //删除共享内存
        {
            perror("shmctl");
            exit(EXIT_FAILURE);
        }
    
        if(semctl(semid,0,IPC_RMID,0)!=0)                            //删除信号量队列
        {
            perror("semctl");
            exit(EXIT_FAILURE);
        }
        return 0;
    }
    sem_shm_recv

     如果没有信号量就要加锁了,防止同时访问一块共享内存


    在Linux的 glibc库 里每个进程都一个独立的IO_FILE_ALL链表,每个节点都是一个IO_FILE_PLUS的对象 , 而默认前三个IO_FILE_PLUS是 0 1 2 ,进程的打开的文件从3开始累加 , 而fd是一个数 这个数代表了第N个IO_FILE_PLUS节点 , fd == 0 则代表 stdin 依此类推 , 如果要在进程间传输fd就必须要把当前进程的IO_FILE_PLUS也发送过去否则 fd  则丢失索引的目标

      6.域套接字

    共享内存虽然可以进行大量的数据传输,但是该如何传输一个fd句柄呢? 

      可以使用local_socket传递fd所映射的的_IO_FILE_PLUS结构的链表(进程打开的文件),每个进程所打开的文件是独立的,传递fd的目的是为了操作同一文件

      而非表一个数字(比方fd == 4 将在_IO_FILE_PLUS链表里的第四个文件) .比方一个处理进程打开一个文件夹里的所有文件判断其文件类型,如果是视频文件则发送给A进程处理 ,如果是一个图片文件则发送给B进程处理

    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    
    //可以传输有效数据也可以传递控制数据 struct msghdr { void *msg_name; /* optional address 指向某个消息(一个地址),可以NULL*/ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array 传递有效数据载体 ,比如数组,数组里有多个arry[0].iov_base */ size_t msg_iovlen; /* # elements in msg_iov 有N个iov*/ void *msg_control; /* ancillary data, see below 传递控制数据的载体 比如数据大小等*/ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ }; struct iovec { /* Scatter/gather array items 可以理解为将数据单元放进一个数组里,当然也不局限于数组*/   void *iov_base; /* Starting address 数据单元*/ size_t iov_len; /* Number of bytes to transfer 单元的有效长度*/ }; struct cmsghdr { socklen_t cmsg_len; /* data byte count, including hdr */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char cmsg_data[]; */ };

        1.使用socket的本地套接字的方式进行传递fd

          local_fd = socket(AF_LOCAL SOCK_STREAM 0);

          

    执行unlink()函数并不一定会真正的删除文件,它先会检查文件系统中此文件的连接数是否为1,如果不是1说明此文件还有其他链接对象,因此只对此文件的连接数进行减1操作。若连接数为1,并且在此时没有任何进程打开该文件,此内容才会真正地被删除掉。在有进程打开此文件的情况下,则暂时不会删除,直到所有打开该文件的进程都结束时文件就会被删除。
     

    总结: 本篇进程通信已经记录了四种分别是管道(有名管道fifo , 无名管道pipe) , 消息队列 , 信号量 , 共享内存 . 后面三种都是一个套路, 创建一个对象所有进程都面向该对象进行通信 

  • 相关阅读:
    Nginx 允许多个域名跨域访问
    mongo 命令
    PyTorch torch.cuda.device_count 返回值与实际 GPU 数量不一致
    APUE 学习笔记 —— 文件I/O
    Django transaction 误用之后遇到的一个问题与解决方法
    如何更新 CentOS 镜像源
    Supervisor 的安装与配置教程
    Sentry的安装搭建与使用
    Python, Django 性能分析工具的使用
    记一次 Apache HUE 优化之因使用 Python 魔术方法而遇到的坑
  • 原文地址:https://www.cnblogs.com/yxnrh/p/12249149.html
Copyright © 2011-2022 走看看