【Linux】IPC进程间通信:管道、共享内存、消息队列、信号量

进程间通信,英文简称IPC,本质上是两个进程之间的数据交换。因为进程具有独立性,每个进程都有自己的虚拟地址空间,访问的都是自己的虚拟地址,因此进程间无法直接通信,需要操作系统提供中间媒介进行通信。
进程间通信分为四种方式:管道、共享内存、消息队列、信号量。其中消息队列和信号量在平时应用较少,在本文中简略介绍。

  • 管道
    管道的本质是内核中的一块缓冲区,管道属于半双工通信,是可以选择方向的单向通信。
    管道自带同步与互斥:
    同步:管道的写操作在不超过PIPE_BUF大小(4096字节),保证原子性。
    互斥:让资源的访问操作按照一定规则进行,避免产生饥饿问题,保证合理操作(管道为空则不能读,管道满了则不能写)。
    管道提供字节流服务:有序的、可靠的、不限制大小的、基于连接的(有读必有写,有写必有读;若读端关闭,继续写会触发异常,导致进程退出;若写端关闭,则读完数据后不再阻塞而是返回0)
    管道的生命周期随进程,在不人为干预下,所有打开管道的进程退出后,管道被释放。

  • 匿名管道:这个内核中的缓冲区没有标识符,无法被其他进程直接找到,只能用于具有亲缘关系的进程间通信。父进程创建一个匿名管道,操作系统会返回一个这个管道的操作句柄(文件描述符)之后父进程创建一个子进程,子进程复制了父进程,也拥有了这个描述符,指向了内核中的同一个管道。
    匿名管道的创建流程:
    ①进程通过系统调用接口在内核中创建管道
    int pipe(int pipefd[2]);
    pipefd[2]用于接收管道的操作句柄,pipefd[0]表示读数据,pipefd[1]表示写输入,成功返回0,失败返回-1。
    ②通过这个接口,操作系统返回管道中的操作句柄。
    ③管道的操作方式类似于文本操作,通过文件描述符及IO接口进行访问。
    ④创建子进程。

  • 命名管道:这个内核中的缓冲区具有标识符(可见于文件系统的管道文件FIFO),所有进程只要知道标识符,就可以通过相同的标识符打开同一个管道进行通信,可以用于同一主机上任意进程间通信。
    int mkfifo(const char*pathname,mode_t mode);
    pathname为管道标识符文件名称
    mode为文件权限
    成功返回0,失败返回-1。
    命名管道的打开特性:
    如果命名管道没有被写的方式打开,则只读打开会阻塞。
    如果命名管道没有被读的方式打开,则只写打开会阻塞。

  • 共享内存
    共享内存本质上是开辟的一块物理内存,多个进程将同一块物理内存映射到自己的虚拟地址空间,通过自己的虚拟地址进行访问,实现数据共享。共享内存是最快的进程间通信方式,相较于其他操作少了用户态与内核态之间的数据拷贝这两个步骤。 共享内存的生命周期随内核。
    共享内存的操作并非是安全的,有可能多个进程同时写入会造成数据交叉或覆盖的危险,因此使用共享内存的操作是需要通过进程同步或互斥来进行保护的。
    共享内存的使用流程:
    ①创建/打开共享内存(添加头文件#include)
    int shmget(key_t key,size_t size,int shmflg);
    key为共享内存标识符
    size为共享内存大小
    shmflg为操作方式,通常为IPC_CREAT|IPC_EXCL,操作权限为0664
    成功返回非负整数,为共享内存在代码中的操作句柄,失败返回-1
    ②将这块共享内存映射到进程的虚拟地址空间
    void * shmat(int shmid,const void*shmaddr,int shmflg);
    shmid为shmget返回的操作句柄
    shmaddr为虚拟空间地址中的映射首地址,一般设置为NULL等系统分配
    shmflg为操作方式,设置为SHM_RDONLY代表只读,否则默认0表示可读可写
    成功返回实际映射在虚拟地址空间的首地址,失败返回-1
    ③具体的内存操作
    例如strcpy/memcpy等
    ④解除进程与共享内存的映射关系
    int shmdt(const void * shmaddr);
    成功返回0,失败返回-1
    ⑤删除共享内存
    int shmctl(int shmid,int cmd,struct_ds * buf);
    shmid为操作句柄
    cmd为具体对共享内存的操作,设置为IPC_RMID表示删除
    buf主要用来设置或获取共享内存的信息,这里直接置为NULL
    对于IPC_RMID操作来说,成功返回0,失败返回-1

  • 消息队列
    消息队列就是在内核中创建了一个优先级队列,多个进程可以通过相同的标识符找到内核中同一个消息队列,通过添加或者获取节点实现数据传输。队列自带同步或互斥,生命周期随内核。

  • 信号量
    信号量本质上就是一个计数器,用于实现进程间的同步或互斥。
    信号量的操作:
    p操作:在临界资源获取之前,先p操作,判断能否获取,能获取-1,不能获取则等待。
    v操作:在资源产生之后,进行v操作,计数+1,唤醒一个等待中的进程。

你可能感兴趣的:(linux,ipc)