天天看点

Linux环境编程Day08-2

进程间通信

基本概念:

    什么是进程间通信:

        是指两个或多个进程之间交互数据的过程,是因为进程之间是相互独立的,为了协同工作的需要必须要交互数据

    进程间通信的分类:

        简单的进程间通信:信号、文件、环境变量、命令行参数

        传统的进程间通信:管道文件

        XSI进程间通信:   共享内存、消息队列、信号量

        网络进程间通信:  套接字Socket

传统进程间通信-管道(FIFO):

    管道是UNIX系统中最古老的进程间通信方式,古老意味着所有系统都支持,早期的管道文件都是半双工,现有的一些系统的管道是全双工的

    管道是一种特殊的文件,它的数据在文件中是流动的,读取之后就会消失,如果文件中没有任何数据读取时会阻塞

    有名管道:基于有文件名的管道文件的通信

        编程模型:

            进程A                   进程B

            创建管道

            打开管道                打开管道

            写数据                  读数据

            关闭管道                关闭管道

            删除管道        

        创建管道文件:

            1、mkfifo filename

            2、函数

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

            功能:创建有名管道文件

            pathname:管道文件路径

            mode:管道文件权限    

    匿名管道:

        注意:匿名管道只适合通过fork创建的父子进程之间使用

        int pipe(int pipefd[2]);

        功能:创建一个匿名管道文件,返回管道文件的读权限fd和写权限fd

        pipefd:返回用于存储管道文件读写fd的数组,输出型参数

                pipefd[0] 用于读   

                pipefd[1] 用于写

        编程模型:

            父进程                  子进程

        获取一对fd                  共享了一对fd

        关闭读                      关闭写

        写数据                      读数据

        关闭写                      关闭读

XSI进程间通信:

    X/Open公司制定用于进程间通信的系统接口

    XSI进程间通信技术都需要借助系统内核,需要创建内核对象,内核对象会以整数形式返回给用户态,相当于文件描述符,也叫做IPC标识符

    文件的创建打开需要借助文件名,同样的,IPC内核对象创建需要借助IPC键值(整数),必须要确保IPC键值是独一无二的

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

    功能:计算出一个独一无二的IPC键值

    pathname:项目路径,不是依靠字符串计算,而是依靠路径的位置以及项目编号计算的,所以不能提供假路径,否则可能会产生同样的IPC键值

    proj_id:项目编号

    返回值:计算出来的IPC键值

共享内存:

    基本特点:

        两个或者多个进程之间共享一块由内核负责维护的内存,该内存可以与多个进程的虚拟内存进行映射

        优点:不需要复制信息,是一种最快的IPC通信机制

        缺点:需要考虑同步访问的问题,一般借助信号来解决

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

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

        key:由进程提供的一个独一无二的IPC键值

        size:共享内存的大小,获取时该参数无意义,一般给0

        shmflg:

            IPC_CREAT   创建共享内存

            IPC_EXCL    共享内存如果已存在,则返回错误

            获取时直接给0

            mode_flags  创建共享内存时需要提供权限 IPC_CREAT|0644

        返回值:IPC标识符,错误时返回-1

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

        功能:让虚拟内存与共享内存进行映射

        shmid:IPC标识符

        shmaddr:想要映射的虚拟内存首地址,为NULL时系统会自动操作

        shmflg:

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

            SHM_RND:只有当shmaddr参数不为NULL时有效,表示对shmaddr参数向下取内存页的整数倍,作为映射地址

            如果都不需要,则写0

        返回值:与共享内存映射后的虚拟内存的首地址,失败返回(void *) -1 或者        0xFFFFFFFF

        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

        buf:

         struct shmid_ds {

               struct ipc_perm shm_perm;    //所有者的相关信息

               size_t          shm_segsz;   //共享内存的字节数

               time_t          shm_atime;   //最后映射的时间

               time_t          shm_dtime;   //最后取消映射的时间

               time_t          shm_ctime;   //最后改变的时间

               pid_t           shm_cpid;    //创建者的进程号

               pid_t           shm_lpid;    //最后映射、取消映射者的进程号

               shmatt_t        shm_nattch;  //当前映射的次数

               ...

           };

        struct ipc_perm {

               key_t          __key;    //创建共享内存的IPC键值

               uid_t          uid;      //当前使用共享内存的用户ID

               gid_t          gid;      //当前使用共享内存的组ID

               uid_t          cuid;     //创建共享内存的用户ID

               gid_t          cgid;     //创建共享内存的用户组ID

               unsigned short mode;     //共享内存的权限

               unsigned short __seq;    //共享内存的序列号

           };

    编程模型:

            进程A                   进程B

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

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

        写数据并通知其他进程     接收到通知后读数据

        接收到通知后读数据      写数据并通知其他进程

        取消映射                取消映射

        删除共享内存            

消息队列:

    基本特点:

        由内核维护管理的数据链表,是通过消息类型收发数据 

    int msgget(key_t key, int msgflg);

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

    key:IPC键值

    msgflg:

        IPC_CREAT   创建消息队列

        IPC_EXCL    如果消息队列已存在则返回错误

        mode:       当创建消息队列时需要提供权限

    返回值:成功返回IPC标识符,失败返回-1

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

    功能:向消息队列发送数据

    msqid:IPC标识符

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

        struct msgbuf {

               long mtype;       // 消息类型

               char mtext[n];    // 数据

           };

    msgsz:数据的字节数,是不包含消息类型的

    msgflg:

        阻塞一般写0

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

    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:    

        IPC_NOWAIT 消息类型不符合时不阻塞,立即返回

        MSG_EXCEPT 如果msgtyp>0,则读取第一个消息类型不是msgtyp的消息

        MSG_NOERROR 如果不包含此标记,则消息的实际长度>msgsz,则会返回错误,并且读取失败,如果包含此标记,则最多读取msgsz个字节,确保一定成功

    返回值:成功读取到的字节数

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

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

    msqid:IPC标识符

    cmd:

        IPC_STAT    获取消息队列的属性 则buf为输出型参数

        IPC_SET     设置消息队列的属性 则buf为输入型参数

        IPC_RMID    删除消息队列       则buf给NULL

    buf:

    struct msqid_ds {

        struct ipc_perm msg_perm;     // 属主信息

        time_t          msg_stime;    // 最后发送时间

        time_t          msg_rtime;    // 最后接收时间

        time_t          msg_ctime;    // 最后修改时间

        unsigned long   __msg_cbytes; // 当前消息队列字节数

        msgqnum_t       msg_qnum;     // 当前的消息数量

        msglen_t        msg_qbytes;   // 消息的最大字节数

        pid_t           msg_lspid;    // 最后发送者的PID

        pid_t           msg_lrpid;    // 最后接收者的PID

    };

    编程模型:

        进程A                       进程B

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

    发送消息                        接收消息

    接收消息                        发送消息

    删除消息队列

继续阅读