Linux环境编程day7

一、进程间通信基本概念

    什么是进程间通信:

        是指两个或多个进程之间需要协同工作、交互数据的过程,因为进程之间是相互独立工作的,为了协同工作就需要进行通信来交互数据

    进程间通信的分类:

        简单的进程间通信:

            信号(携带附加信息)、文件、环境变量、命令行参数等

        传统的进程间通信:

            管道文件(有名管道、匿名管道)

        XSI的进程间通信:

           共享内存、消息队列、信号量

        网络的进程间通信:

            socket套接字

二、传统的进程间通信技术-管道

    管道是UNIX系统中最古老的进程间通信方式,古老就意味着所有的系统都支持,早期的管道是半双工,现在有些系统的管道是全双工(假定以半双工来编写代码)

    管道是一种特殊的文件,它的数据在管道文件中是流动的,读取后就会自动消失,如果文件中没有数据要读取会阻塞等待

    有名管道:

        基于有文件名的管道文件的进程间通信,可以是任何进程之间的通信

        编程模型:

            进程A               进程B

          创建管道               ...

          打开管道             打开管道

          写数据                读数据

          关闭管道             关闭管道

          删除管道               ...

   

        创建有名管道文件

        命令: mkfifo filename

        函数:int mkfifo(const char *pathname, mode_t mode);

        功能:创建有名管道文件

        pathname:文件的路径

        mode:文件的权限掩码

    匿名管道:

        没有名字的管道文件

        只适合通过fork创建的有血缘关系的进程间通信(父子进程、兄弟进程之间)

        int pipe(int pipefd[2]);

        功能:创建并打开一个匿名管道文件

        pipefd:输出型参数 返回该匿名管道文件的读权限fd和写权限fd

            pipefd[0]   用于读管道文件

            pipefd[1]   用于写管道文件

       

        编程模型:

            父进程                    子进程

           获取一对fd               ...

           创建子进程             共享一对fd

          关闭读fd[0]              关闭写fd[1]

          fd[1]写数据              fd[0]读数据

          关闭fd[1]                  关闭fd[0]

三、XSI进程间通信

    XSI是X/open 公司制定的用于进程间通信的系统(S)接口(I)标准

    XSI进程间通信标准都需要借助系统内核完成,需要创建内核对象,内核对象以整数形式返回给用户,相当于文件描述符\文件指针,

    代表了某个内核对象完成某次进程间通信任务,也叫做IPC标识符

   

    类似文件的创建需要借助文件名一样,IPC标识符的创建也需要借助IPC键值(整数),也跟文件名一样,要确保IPC键值是独一无二的

    一般是通过函数生成一个独一无二的IPC键值

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

    功能:生成一个独一无二的IPC键值

    pathname:项目路径

    proj_id:项目编号

    返回值:根据项目路径+项目编号自动计算出IPC键值

    注意:计算IPC键值的方式不是根据pathname的字符串内容,而是依靠路径的位置,如果提供了假的路径,不管编号如何,都会得到相同的IPC键值,不正确

    共享内存:

        基本特点:

            两个或多个进程共享同一块由内核负责维护的物理内存,该物理内存是需要与进程的虚拟内存进行映射后使用

        优点:不需要进行磁盘读写操作,无需复制操作,是最快的一种IPC机制

        缺点:需要考虑通信的同步问题,一般采用信号解决

        int shmget(key_t key, size_t size, int shmflg);

        功能:创建\获取共享内存

        key:IPC键值

        size:共享内存的大小,当是获取时此参数无意义写0即可

        shmflg:

            IPC_CREAT   创建共享内存,已存在时会直接获取

            IPC_EXCL    配合CREAT,如果已存在则返回错误

            获取时直接给0即可

            注意:如果是创建IPC_CREAT,需要在后面额外按位或这段共享内存的读写权限IPC_CREAT|0644

        返回值:成功返回该共享内存的IPC标识符,失败返回-1

        void *shmat(int shmid, const void *shmaddr, int shmflg);

        功能:虚拟内存与物理共享内存映射

        shmid:IPC标识符

        shmaddr:想要映射的虚拟内存首地址,一般给NULL就会自动安排

        shmflg:

            SHM_RND 只有当shmaddr不是NULL时才有效,当映射的字节数不足一页的整数倍时,会向下取整数倍的页数来映射

            SHM_RDONLY  以只读方式映射共享内存

            如不需要以上操作,一般给0即可

        返回值:成功返回映射的虚拟内存的首地址,失败返回(void*)-1

        int shmdt(const void *shmaddr);

        功能:取消映射

        shmaddr:映射过的虚拟内存首地址

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);

        功能:删除/控制共享内存

        shmid:IPC标识符

        cmd:

            IPC_STAT    获取共享内存属性信息  buf输出型参数

            IPC_SET     修改共享内存的属性 buf输入型参数

            IPC_RMID    删除共享内存  buf给NULL即可

       

        编程模型:

            进程A                                    进程B

         创建共享内存                       获取共享内存

         映射共享内存                       映射共享内存

         写数据到内存并通知             接到通知就读内存数据

         接到通知就读内存数据          写数据到内存并通知

         取消映射                                取消映射

         删除共享内存

    消息队列:

        基本特点:是一个由内核维护管理的数据链表,通过消息类型来匹配正确后收发数据

        int msgget(key_t key, int msgflg);

        功能:创建\获取消息队列

        key:IPC键值

        msgflg:

            IPC_CREAT   创建消息队列,已存在时会直接获取

            IPC_EXCL    配合CREAT,如果已存在则返回错误

            获取时直接给0即可

            注意:如果是创建IPC_CREAT,需要在后面额外按位或这段消息队列的读写权限IPC_CREAT|0644

        返回值:成功返回该消息队列的IPC标识符,失败返回-1

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

        功能:向消息队列发送消息包

        msqid:IPC标识符

        msgp:要发送的消息包的首地址

            struct msgbuf {

               long mtype;  //第一个成员必须是long类型的消息类型

               char mtext[1];   //根据需要存储数据

           };// 该结构由程序员自己设计

        msgsz:只需要数据的字节数,不包括消息类型

        msgflg:

            阻塞一般写0

            IPC_NOWAIT  当消息队列满时,不等待立即返回

        返回值:成功0 失败-1

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

        功能:从消息队列中接收读取数据

        msqid:IPC标识符

        msgp:接收消息包内存首地址

        msgsz:数据内存的字节数大小,不包括消息类型,因为不确定对方发送的字节数,因此建议给大一点

        msgtyp:要接收的消息类型

            >0  读取消息类型等于msgtyp的消息

            =0  读取消息队列中的第一条消息

            <0  读取消息队列中的消息类型小于等于abs(msgtyp)的消息如果同时有多个,则读取最小的那一个

        msgflg:

            阻塞等待一般给0即可

            IPC_NOWAIT  如果当时消息类型没有匹配,则不阻塞立即返回

        返回值:成功返回读取到的数据字节数,失败-1

   

        int msgctl(int msqid, int cmd, struct msqid_ds *buf);

        功能:删除/控制消息队列

        msqid:IPC标识符

        cmd:

            IPC_STAT    获取消息队列属性信息  buf输出型参数

            IPC_SET     修改消息队列的属性 buf输入型参数

            IPC_RMID    删除消息队列  buf给NULL即可

        编程模型:

            进程A                            进程B

          创建消息队列              获取消息队列

          发送消息msg-a           接收msg-a消息

          接收msg-b消息           发送消息msg-b

          删除消息队列

   

    信号量:

        基本特点:由内核维护的一个"全局变量",用于记录进程之间共享资源的数量,限制进程对共享资源的访问

        信号量更像是一种数据操作锁,本身是不具备数据交换功能,而是通过控制其他的通信资源

        (共享内存、消息队列、文件等)来实现进程间通信的协调

        1、如果信号量的值>0,说明有可以使用的资源,使用时需要将信号量-1,然后再使用

        2、如果信号量的值等于0,说明没有资源可以使用,此时进程会进入休眠,直到信号量的值大于0,进程就会被唤醒,执行步骤1

        3、当资源使用完毕后,需要将信号量+1,正在休眠的进程就可以被唤醒执行步骤1

        int semget(key_t key, int nsems, int semflg);

        功能:创建\获取信号量的IPC标识符

        key:IPC键值

        nsems:信号量的数量

        semflg:

            IPC_CREAT   创建信号量,已存在时会直接获取

            IPC_EXCL    配合CREAT,如果已存在则返回错误

            获取时直接给0即可

            注意:如果是创建IPC_CREAT,需要在后面额外按位或这段信号量的读写权限IPC_CREAT|0644

        返回值:成功返回该信号量的IPC标识符,失败返回-1

        int semop(int semid, struct sembuf *sops,size_t nsops);

        功能:对某个或某些信号量进行加减操作

        semid:IPC标识符

        sops:

            struct sembuf{

            unsigned short sem_num;  // 信号量的下标

            short          sem_op;   //

                    1   信号量+1

                    -1  信号量尝试-1,如果为0则休眠阻塞

                    0   等待信号量的值为0,否则阻塞休眠

   

            short          sem_flg;

                    0           阻塞

                    IPC_NOWAIT  不阻塞

                    SEM_UNDO  如果进程终止了没有手动还原信号量+1,系统会自动还原+1  

            };

        nsops:表示sops指针指向多少个连续的结构体,意味着要加减多少个信号量,一般每次只操作一个时写1即可

        int semctl(int semid, int semnum, int cmd, ...);

        功能:删除\控制信号量

        semid:IPC标识符

        semnum:第几个信号量 下标从0开始

        cmd:

            IPC_STAT   获取信号量属性

            IPC_SET    设置信号量属性

            IPC_RMID   删除信号量

            GETALL      获取所有信号量的值

            GETNCNT     获取正在等待减信号量的进程数

            GETVAL     通过返回值获取下标为semnum信号量的值

            GETZCNT    获取正在等待信号量的值为0的进程数

            SETVAL      设置下标为semnum信号量的值

            SETALL      设置所有信号量的值

        ...:

        union semun {

               int              val;    // 用于设置信号量的值

               struct semid_ds *buf;// 用于获取\设置信号量的属性

               unsigned short  *array;  //用于批量设置\获取信号量的值

           };

        使用流程:

            1、创建信号量

            2、设置信号量管理的资源数

            3、减\加信号量

            4、删除信号量

你可能感兴趣的:(linux)