day06

进程间通信:
    一、基本概念:
        什么是进程间通信:
            指两个或多个进程之间需要协同工作、交互数据的过程,因为进程之间是相互独立工作的,为了协同工作就需要进行通信来交互数据
        进程间通信的分类:                                   **(背)**
            简单的进程间通信:
                信号(携带附加信息)、文件、环境变量表、命令行参数
            传统的进程间通信:
                管道文件(有名管道、匿名管道)
            XSI的进程间通信:
                共享内存、消息队列、信号量
            网络的进程间通信:
                socket套接字

    二、传统的进程间通信技术——管道
        管道是UNIX系统中最古老的进程间通信方式,古老就意味着所有的系统都支持,早期的管道是半双工,现在有些系统的管道是全双工(假定以半双工来编写代码)
        管道是一种特殊的文件,它的数据在管道文件中是流动的,读取后就会自动消失,如果文件中没有数据要读取会阻塞等待

        有名管道:
            基于有文件名的管道文件的进程间通信,可以是任何进程之间的通信
            *编程模型:
                    进程A           进程B
                   创建管道          ...
                   打开管道         打开管道
                    写数据          读数据
                   关闭管道         关闭管道
                   删除管道          ...

                1、创建有名管道文件的方式:
                    命令:mkfifo filename
                    函数:mkfifo
                        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         关闭写fd
                  fd[1]写数据      fd[0]读数据
                   关闭fd[1]        关闭fd[0]

三、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    配合IPC_CREAT,如果已存在则返回错误
                0           只获取
                注意:如果是创建IPC_CREAT,需要在后面额外按位或这段共享内存的读写权限
        返回值:创建成功返回该共享内存的IPC标识符,失败-1

        void *shmat(int shmid, const void *shmaddr, int shmflg);
        功能:虚拟内存与物理内存共享内存映射
            shmid:IPC标识符
            shmaddr:想要映射的虚拟内存首地址,一般给NULL自动映射
            shmflg:
                SHM_RND     只有当shmaddr不是NULL时才有效,当映射的字节数不足一页的整数倍时,会向下取整数倍的页数来映射
                SHM_RDONLY  以只读方式映射内存
                如果不需要以上操作,一般给0即可
        返回值:成功返回映射后虚拟内存的首地址,失败返回0xffff ffff || (void*)-1
        int shmdt(const void *shmaddr);
        功能:取消映射
            shmaddr:   映射过的虚拟内存首地址
        返回值:成功返回0,失败返回-1
        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    配合IPC_CREAT,如果已存在则返回错误
                0           只获取
                注意:如果是创建IPC_CREAT,需要在后面额外按位或这段共享内存的读写权限
        返回值:创建成功返回该消息队列的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:PID标识符
            msgp:存储数据部分内存首地址
            msgsz:数据内存的字节数大小,不包括消息类型,因为不确定对方发送的字节数,建议给大一点
            msgtyp:要接收的数据类型
                >0  读取消息类型=msgtyp的消息
                =0  读取消息队列中的第一条消息
                <0  读取消息队列中的消息类型小于等于abs(msgtyp)的消息
                    如果同时有多个读取最小的
            msgflg:
                IPC_NOWAIT  如果当时消息类型没有匹配,则不阻塞立即返回
                阻塞等待一般给0即可
        返回值:成功返回读取到的数据字节数,失败-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);
    功能:创建、获取信号量
        key:IPC键值
        nsems:信号量的数量
        semflg
                IPC_CREAT   创建、获取消息队列,已存在时会直接获取
                IPC_EXCL    配合IPC_CREAT,如果已存在则返回错误
                0           只获取
                注意:如果是创建IPC_CREAT,需要在后面额外按位或这段共享内存的读写权限
        返回值:创建成功返回该信号量的IPC标识符,失败-1

        int semop(int semid, struct sembuf *sops, size_t nsops);
        功能:对信号量进行加减操作
            semid:IPC标识符
            sops:
                struct sembuf, containing the following members:
                    unsigned short sem_num;  /* 信号量的下标 */
                    short          sem_op;   //
                        1   信号量+1
                        -1  信号量尝试-1,如果为0则休眠阻塞
                        0   等待信号量的值为0,否则阻塞休眠
                    short          sem_flg;  /* operation flags */
                        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、删除信号量
posted @   歪爱慕外  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示