操作系统:06 进程通信

1 基本概念

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

2 进程间通信的分类

2.1 简单的进程间通信:

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

2.2 传统的进程间通信:

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

2.3 XSI的进程间通信:

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

2.4 网络的进程间通信:

        套接字技术Socket

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

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

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

3.1 有名管道

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

3.1.1通信编程模型:

        进程A                   进程B

      创建管道文件           ...

      打开文件             打开文件

      写数据                  读数据

      关闭管道             关闭管道

      删除管道文件           ...

3.1.2创建有名管道:

命令: mkfifo filename

函数:

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

        功能:创建有名管道文件

        pathname:管道文件的路径

        mode:文件权限

  3.2匿名管道:

        注意:只适合通过fork创建的父子进程之间通信

    int pipe(int pipefd[2]);

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

        通过参数pipefd,返回该匿名管道文件的读文件描述符和写文件描述符

        pipefd:存储读写fd的数组,输出型参数

        pipefd[0] 用于读  pipefd[1] 用于写

3.2.1 匿名管道的编程模型:

            父进程                     子进程

   创建获取匿名管道         ...

        创建子进程              共享一对fd

           关闭读                     关闭写

           写数据                     读数据

           关闭写                      关闭读

3.2.2 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键值一定相同,那就没意义了

    如果是有效路径,只要两个参数出现一个不同,那么就会计算出不相同的IPC键值,就有意义了

4 共享内存

4.1 基本特点

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

        优点:操作简单,不需要读写磁盘、不需要复制,最快的一种XSI机制

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

4.2 使用

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

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

    key:IPC键值 ftok给

    size:共享内存的大小,只有获取共享内存时此参数无意义就设置为0

    shmflg:

        IPC_CREAT 创建共享内存,已存在时则获取

        IPC_EXCL  共享内存已存在时则返回失败

        如果是获取直接赋0

        注意:当创建共享内存时,需要额外提供该段共享内存的权限码,

            例如shmget(xx,4096,IPC_CREAT|0644)

    返回值:IPC标识符,是创建/获取得到的共享内存的标识,错误会返回-1(只要不是-1就代表创建/获取成功)

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

    shmid:IPC标识符

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

    shmfig:

        SHM_RND 当shmaddr不为NULL时才有效,标识从shmaddr开始取内存页的整数倍进行映射,提高内存读写效率

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

    返回值:成功映射后的内存首地址,映射失败返回0xFFFFFFFF(-1)

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

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

    shmid:IPC标识符

    cmd:

        IPC_STAT    获取共享内存的属性数据  buf输出型参数(获取到的属性数据存储到buf里)

        IPC_SET     设置共享内存的属性数据  buf输入性参数(使用者将要设置的属性数据存到buf里)

        IPC_RMID    删除共享内存(IPC也会删掉,统统删光)

    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

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

        shmatt_t        shm_nattch;  //当前映射次数

    };

    int shmdt(const void *shmaddr);

    功能:取消虚拟内存与共享内存的映射

    shmaddr:虚拟内存的首地址

4.3 编程模型

            进程A                                    进程B

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

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

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

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

         取消映射                               取消映射

         删除共享内存                             ...  

5 消息队列

5.1 基础概念

        由内核负责维护管理的数据链表结构,通过消息类型来对应的收发数据

5.2 使用

    int msgget(key_t key, int msgflg);

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

    key:IPC键值 ftok给

    shmflg:

        IPC_CREAT 创建消息队列,已存在时则获取

        IPC_EXCL  消息队列已存在时则返回失败

        如果是获取直接赋0

        注意:当创建消息队列时,需要额外提供该消息队列的权限码,

    返回值:IPC标识符,是创建/获取得到的共享内存的标识,错误会返回-1(只要不是-1就代表创建/获取成功)

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

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

    msqid:IPC标识符

    msgp:消息包结构首地址

            struct msgbuf {

               long mtype;       //消息类型,必须>0

               char mtext[n];    //消息数据

           };

    msgsz:消息数据的字节数即mtext的字节数

    msgflg:

        0   阻塞发送

        IPC_NOWAIT  当消息队列满时,立即结束并返回,

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

    功能:从消息队列中接收对应的消息包中的数据

    msqid:IPC标识符

    msgp:存储读取到的消息包结构首地址

        struct msgbuf {

            long mtype;       //消息类型,必须>0

            char mtext[n];    //消息数据

        };

    msgsz:消息包结构体字节数

    msgtyp:要接收的消息类型几号

        0   读取当前消息队列中的第一条消息包  

        >0  读取消息类型为msgtyp的消息包

        <0  读取消息类型

    msgflg:一般写0

        IPC_NOWAIT如果没有符合的消息包,则立即返回不阻塞    

        MSG_EXCEPT 如果msgtyp>0,则读取第一个值不等于msgtyp的消息包

        MSG_NOERROR 如果不包含此标志,实际消息包的字节数>msgtyp时会返回错误并不读取,如果包含,则不报错并只读取前msgtyp个字节

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

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

    shmid:IPC标识符

    cmd:

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

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

        IPC_RMID    删除消息队列          buf无意义NULL

    buf:

        消息队列属性结构体

5.3 编程模型

       进程A                               进程B

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

      发送消息                         接收消息

    接收不同的消息包       发送不同的消息包

      删除消息                              ...

6 信号量

6.1基本特点

        由内核来维护共存给若干个进程的“全局变量”,用于记录共享资源的数量,限制进程对共享资源的访问

        信号量是一种数据操作锁,本身不具备完整的数据通信交换功能而是通过控制其他的通信资源来更好地实现进程间通信

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

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

    3、当资源使用完毕后,先把信号量的值+1,然后内核会唤醒正在休眠的进程

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

    功能:创建/获取信号量

    key:IPC键值

    nsems:信号量整体个数,一般写1

    semflg:

    返回值:IPC标识符

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

    功能:对信号量的值进行操作

    semid:标识符

你可能感兴趣的:(操作系统,unix,linux,服务器)