Linux环境编程——进程间通信

一、基本概念

什么是进程间通信:是指两个或多个进程之间交互数据的过程,因为进程之间是相互独立的,为了进程间协同工作就必须实现进程间交互数据
进程间通信的分类:
    简单的进程间通信:信号、普通文件、环境变量表、命令行参数
    传统的进程间通信:管道文件
    XSI的进程间通信:共享内存、消息队列、信号量
    网络进程间通信:Socket套接字

二、传统的进程间通信-管道文件

管道是UNIX系统中最古老的进程间通信技术,古老意味着所有系统都支持,早起的管道是半双工通信,先有的系统管道是全双工通信
管道就是一种特殊的文件,数据在文件中是流动的,读取之后就自动消失,如果文件中没有数据则会阻塞

有名管道:基于有文件名的管道文件的通信
    编程模型
        进程A           进程B
      创建管道         
      打开管道          打开管道
       写数据            读数据
      关闭管道          关闭管道
      删除管道

    创建有名管道文件:
    1、命令 mkfifo
    2、函数
    #include 
    #include 
    int mkfifo(const char *pathname, mode_t mode);
    功能:创建有名管道
    pathname:管道文件的路径
    mode:管道文件的权限 0664

匿名管道:只适合通过fork创建父子进程之间使用
#include 
int pipe(int pipefd[2]);
功能:创建一个匿名管道文件
通过pipefd返回该匿名管道文件的读权限fd和写权限的fd
    pipefd[0]   用于读匿名管道
    pipefd[1]   用于写匿名管道

    编程模型:
        父进程          子进程
      获取一对fd       
      创建子进程       拷贝\共享一对fd
      关闭读             关闭写
      写数据             读数据
      关闭写             关闭读

三、XSI进程间通信

X/open公司制定的用于进程间通信的系统(S)接口(I)
XSI进程间通信都需要借助系统内核完成,需要创建内核对象,内核对象会以整数的形式返回给用户态,类似于文件描述符,也叫作IPC标识符
文件的创建打开需要借助文件名,IPC内核对象需要借助IPC键值(整数),必须要确保IPC键值是独一无二的
   #include 
   #include 
   key_t ftok(const char *pathname, int proj_id);
   功能:计算出一个独一无二的IPC键值
   pathname:项目路径
   proj_id:项目编号
   返回值:计算出来的IPC键值
   注意:项目路径必须真是存在,否在计算出来的key永远相同

共享内存:

基础特点:
    两个或多个进程之间共享一块由内核负责统一管理的内存,该内存可以与多个进程的虚拟内存进行映射
    优点:不需要复制信息,直接读写内存,是最快的一种IPC机制
    缺点:需要考虑同步访问问题,一般使用信号
    #include 
    #include 
    int shmget(key_t key, size_t size, int shmflg);
    功能:创建、获取一块共享内存
    key:IPC键值、
    size:共享内存的大小,获取共享内存时此参数无意义,一般给0
    shmflg:
        IPC_CREAT   创建共享内存,如果已存在直接获取
        IPC_EXCL    共享内存已存在,返回失败 
        获取时直接给0
        注意:如果是创建内存要需要额外提供共享内存的权限
        例如:IPC_CREAT|0664
    返回值:IPC标识符,失败-1


    #include 
    #include 
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:让虚拟内存与共享内存进行映射
    shmid:IPC标识符 shmget的返回值
    shmaddr:想要映射的虚拟内存首地址,为NULL时系统会自动分配地址
    shmflg:
        SHM_RDONLY  以只读方式映射共享内存
        SHM_RND:只有shmaddr参数不为NULL时才有效,表示从shmaddr开始向下以整数页方式映射
    返回值:与共享内存映射成功后的虚拟内存首地址,失败返回(void*)-1

    int shmdt(const void *shmaddr);   
    功能:取消映射
    shmaddr:映射成功后的虚拟内存首地址
    返回值:0成功-1失败

    #include 
    #include 
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    功能:删除/控制共享内存
    shmid:IPC标识符
    cmd:
        IPC_STAT:获取共享内存的属性,buf输出型参数
        IPC_SET: 设置共享内存的属性,buf输入型参数
        IPC_RMID:删除共享内存          NULL
    buf:

编程模型:
    进程A           进程B
创建共享内存       获取共享内存
映射共享内存       映射共享内存
写数据通知其他进程    收到通知读数据
收到通知读数据    写数据通知其他进程
  取消映射            取消映射
  删除共享内存

消息队列:

由内核负责维护管理的链式数据队列,不是根据先后顺序出队,而是根据消息类型进行首发数据
    
    #include 
    #include 
    #include 
    int msgget(key_t key, int msgflg);
    功能:创建\获取消息队列
    key:IPC键值 
    msgflg:
        IPC_CREAT   消息队列已存在则获取,否侧创建
        IPC_EXCL    消息队列已存在则返回错误
        注意:如果创建需要提供权限 
    返回值:成功返回IPC标识符,失败返回-1

    #include 
    #include 
    #include 
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    功能:向消息队列发送消息包
    msqid:IPC标识符
    msgp:要发送的消息包的首地址
        struct msgbuf {
           long mtype;       //消息类型
           char mtext[1];    //数据
        };
    
    msgsz:数据(mtext[1])的字节数,不包含消息类型
    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 如果不包含此标志,如果实际发送过来的数据字节数>接收的字节数,则返回失败,如果包含此标志,那么就只读接收到的字节数,一定会成功
    返回值:成功读取到的字节数

    #include 
    #include 
    #include 
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    功能:获取\修改消息队列的属性、删除队列
    msqid:IPC标识符
    cmd:
        IPC_STAT:获取共享内存的属性,buf输出型参数
        IPC_SET: 设置共享内存的属性,buf输入型参数
        IPC_RMID:删除共享内存          NULL
    buf:

    编程模型:
        进程A               进程B 
    创建消息队列           获取消息队列 
      发送消息               获取消息 
      获取消息               发送消息 
    删除消息队列

信号量:

基本特点:由内核管理的一个“全局变量”,用于记录共享资源的数量,限制进程对共享资源的访问使用
信号量设计一种数据操作锁,本身是不具备数据交互功能,而是通过控制其他的通信资源从而配合实现进程间通信
1、如果信号量的值大于0,说明可以使用资源,使用时需要信号量-1,然后再使用
2、如果信号量的值等于0,说明没有资源可使用,此时进程进入休眠,直到信号量的值大于0,进程会被唤醒,执行步骤1
3、当资源使用完毕,把信号量的值+1,正在休眠的进程就会被唤醒

#include 
#include 
#include 
int semget(key_t key, int nsems, int semflg);
功能:创建\获取信号量
key:IPC键值
nsems:型号量的数量 一般写1
msgflg:
        IPC_CREAT   信号量已存在则获取,否侧创建
        IPC_EXCL    信号量已存在则返回错误
        注意:如果创建需要提供权限
返回值:IPC标志符,失败-1

int semctl(int semid,int semnum,int cmd, ...);
功能:删除、控制型号量
semid:IPC标识符
semnum:要操作的第几个型号量,从0开始,下标
 cmd:
        IPC_STAT:获取信号量的属性,buf输出型参数
        IPC_SET: 设置信号量的属性,buf输入型参数
        IPC_RMID:删除信号量内存          NULL
        SETVAL    设置某个信号量的值
        SETALL    设置所有信号量的值
        GETVAL    获取某个信号量的值
        GETALL    获取所有信号量的值
        GETNCNT   获取等待拿资源的进程数量

int semop(int semid, struct sembuf *sops, size_t nsops);
功能:对信号量进行加减操作
semid:IPC标识符
    sembuf{
       unsigned short sem_num;  //信号量的下标
       short          sem_op;   // 1 信号量+1  -1 信号量-1 如果不能间,则默认阻塞
       short          sem_flg;  //IPC_NOWAIT 不阻塞         SEM_UNDO 如果进程终止没有手动还资源,系统会自动还
    }

你可能感兴趣的:(linux,unix,网络)