linux 进程间通信的几种方式

时间:2021-06-10 00:14:11

一 管道技术
   1)概念
   历史上是半双工的,只能单向流动,目前有系统提供全双工管道
   linux上全双工管道由unix域套接字实现
   只能在具有公共祖先的进程间使用
   2)shell 中的命令序列 中 标准输入输出的重定向就是通过管道连接的
   3)创建管道
     pipe(int fields[2])
     返回: fields[0]为读描述符,fields[1]为写描述符
           fields[0]的数据来源于fields[1]
   4)fstat函数对文件描述符返回fifo类型
   5)由于某些系统不支持全双工管道,所以创建管道之后,需要将某些管道描述符关闭以用单向的管道
   6)读写规则
     当读一个写端已经关闭的管道,在所有数据读完之后,read返回0
     当写一个读端已经关闭的管道,则产生信号SIGPIPE,忽略信号或者从信号处理函数返回后,write返回-1,errno =EPIPE
   7)管道的最大值
     fpathconf,pathconf返回PIPE_BUF的值
     
二 popen与pclose技术 (pipe open,  pipe close)
   目的:执行一个程序获得其输出,或者向一个进程写入东西
   1)来自于标准IO库
     file* popen(const char * cmdstring, const char * type)
     pclose(file *)
   2) popen执行fork,然后exec cmdstring,并且返回一个标准io文件指针
      type : "r" 文件指针连接到exec进程的标准输出
           : "w" 文件指针连接到exec进程的标准输入
   3)标准输出通常是行缓冲的,如果不包含换行符,需要再调用fflush函数冲洗到屏幕
   
   
三 协同进程
    目的:比popen更强,popen只能单边读或单边写入目的进程
          协同进程可以向一个进程读写
    概念: 当一个程序产生某个过滤程序的输入,同时又读取过滤程序的输出,则该过滤程序被成为协同进程 coprocess
    
   
四  FIFO命名管道
    可用于不相关的进程
    1)创建命名管道
      mkfifo(const char*, mode_t mode)
    2)mode需要区分O_NONBLOCK  O_BLOCK
      如果没有指定非阻塞模式: 写管道的进程会阻塞到相对应的读管道的进程, 读管道的进程会阻塞到写管道的进程
      如果指定了阻塞模式,读操作会立即返回, 如果没有读管道的操作,写管道的进程会直接返回错误
    3)写一个没有绑定读进程的管道,会产生信号SIGPIPE
    4)FIFO的最大数据量是PIPE_BUF
    5)命名管道的2个用处:
      a) shell命令中,无需临时文件,将数据从1条管道线发送到另外1条管道线
      b) 用于在客户端和服务器之间传送数据
      
      
五  三种XSI IPC
     1)消息队列
     2)信号量
     3)共享存储
     4)标识符是ipc对象内部名,键key与每个ipc对象关联,键作为外部名
     5)键的数据类型key_t
     6)创建ipc结构的方法 msgget semget  shmget
     7)ftok将路径及id 转换成唯一的键名
     8)ipc结构体里面存有类型ipc_perm,该类型规定了权限和所有者
     9)可以通过msgctl,semctrl,shmctrl来修改ipc结构体的权限
     10)IPC的缺点:
        创建了需要显示的去读取或删除数据,否则一直存在
        ipc结构在文件系统中没有名字,不支持参加的文件命令
        ipc不使用文件描述符,所以不能使用轮询,一次使用处理多个ipc结构
        
六   消息队列
     1)概念 消息的链接表, queue, 标识符为queue id
     2)创建或打开一个新的队列  msgget(key_t key, int flag),返回队列ID
     3)追加消息到队列尾部
       msgsnd(int msqid, const void* ptr, size_t nbytes, int flag)
       消息内容结构体 struct mymesg
       flag : IPC_NOWAIT 如果消息队列已满,则立即出错返回
              没有指定IPC_NOWAIT 进程被阻塞,直到有空间,系统删除了队列,捕捉到一个信号,并从信号处理程序返回
              
     4)消息内容: 类型字段 + 长度 + 内容
     5)取出消息 msgrcv   可以按先进先出或者消息的类型2种方式取消息
       msgrcv(int msqid, void* ptr, size_t nbytes, long type, int flag)
       参数flag设置了MSG_NOERROR  而消息长度大于了nbytes 则消息被截断返回,
           否则返回E2BIG,消息不取出来
       参数type指定了想要那一种消息
             type ==0  返回队列中的第一个消息
             type>0 返回队列中消息类型为type的第一个消息
             type <0 返回消息类型小于或等于type绝对值的消息中的最小值的消息
     6)队列的当前状态 struct msqid_ds
     7)消息队列控制函数msgctrl(int msqid, int cmd, struct msqid_ds* buf)
       cmd        说明
       IPC_STAT   取消息队列的msqid_ds结构
       IPC_SET    设置buf为消息队列的msqid_ds结构
       IPC_RMID   删除消息队列及其队列中的数据
     **由于消息队列具有的问题,所以apue里面推荐不要在应用程序里面使用
      
七   信号量(semaphore)
    1)概念: 用于多进程对共享数据访问的一个计数器
    2)获得共享资源的步骤:
      a)测试控制该资源的信号量
      b)新信号量的值为正,则进程可以使用该资源,进程将信号量减1,表示它正在使用
      c)若信号量的值为0,则进程进入休眠状态
      d)进程释放共享资源的时候,信号量加1,唤醒休眠的进程
    3)二元信号量或双态信号量是最常见的信号量形式,可以被初始为任一正值
    4)信号量集关联到一个semid_ds结构
    5)创建信号量 semget(key_t, int nsems, int flag)
    6) semctl 设置信号量
    7) semop 设置信号量的值

    8)信号量操作sembuf结构体


sem_t *sem_open(const char *name, int oflag);
       sem_t *sem_open(const char *name, int oflag,
                       mode_t mode, unsigned int value);

 #include <semaphore.h>

       int sem_wait(sem_t *sem);
       int sem_trywait(sem_t *sem);
       int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

       int sem_post(sem_t *sem);

DESCRIPTION
       sem_post()  increments  (unlocks)  the semaphore pointed to by sem.  If the semaphore's value
       consequently becomes greater  than  zero,  then  another  process  or  thread  blocked  in  a
       sem_wait(3) call will be woken up and proceed to lock the semaphore.

RETURN VALUE
       sem_post()  returns  0 on success; on error, the value of the semaphore is left unchanged, -1
       is returned, and errno is set to indicate the error.

      
八  共享存储
   1)概念: 允许2个或更多进程共享一给定的存储区
   2)每个共享存储关联到一个结构 struct shmid_ds,实际上这个结构体是共享内存的属性
   3)创建共享存储 shmget(key_t,size_t,int)
   4)设置共享存储 shmctl
   5)绑定到当前进程的某个地址 ,方便使用 shmat
   6)删除绑定 shmdt
   7)删除标识符(键)shmctl + IPC_RMID

   8)linux*享存储紧邻栈下面

   9)共享内存的数据同步由信号量来实现

int shmget(key key,size_t size,int flag) 返回共享内存的标识iD(shmid)

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

void* shmat(int shmid, const void * addr, int flag)

int shmdt(void * addr)