【Linux】Linux进程间通信的方法

来源:互联网 发布:淘宝的古着衣服能穿吗 编辑:程序博客网 时间:2024/04/30 15:48
Linux进程间通信的方法概述为以下七种:
1、管道(pipe)
2、有名管道(named pipe)
3、信号量
4、消息队列
5、信号
6、共享内存
7、套接字
-------------------------------------------------------------------------------
linux进程间通信

      1. 管道。

             在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:
  ·    1) 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。
  ·   2)读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。
  注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。
  我们可以使用管道符|来连接进程.在Linux系统中,由管道连接起来的进程可以自动运行,就如同在他们有一个数据流一样.在下面的这个例子中,我们要使用sort命令来排序ps的输出.而如果我们不使用管道,我们就要分几步来完成:
  $ ps > psout.txt
  $ sort psout.txt >pssort.out
  一个更好的办法就是可以用管道来处理:
  $ ps | sort > pssort.out
  因为我们要在屏幕上看到他们,我们要使用第三个进程:
  $ ps | sort | more
  使用的管道数并没有一个量的限制.如果我们要看到正在运行的除了shell以外的不同名字的进程,我们可以用下面的命令:
  $ ps -xo comm | sort | uniq | grep -v sh | more
  在这个命令中,使用了ps的输出,将这个输出以字母的顺序进行排序,使用uniq来解压进程,使用grep -v sh来移除名为sh的进程,最后在屏幕上显示结果.
  在这里我们就可以看到,这样的方式式要比单个执行的命令好得多.在这里我们要注意的一点点就是,在这个命令中我们不要两次使用同一个文件.如下面的命令:
  $ cat mydate.txt | sort | uniq | >mydate.txt
  这样我们就会得到一个空文件,因为在我们读取这个之前已经改写了这个文件.

pipe

  Linux C 中的管道函数 pipe
  #include <unistd.h>
  函数原型: int pipe(int fd[2])

linux或unix 管道命令

  用unix所谓的管道 可以把一个进程的标准输出流与另一个进程的标准输入流连接起来
  unix中欧你多大许多命令被设计为过滤器 从标准输入中读取输入 将输出传送到 标准输出
  bash用“|” 在两个命令之间创建管道
2. FIFO特别文件
  有名管道,作为特别文件存储于文件系统中。有名管道一旦建立就存在于文件系统中,除非显示的unlink
  #include<sys/tpes.h>
  #include<sys/stat.h>
  int mknod(const char *path,mode_t mod,dev_t dev);
  int mkfifo(const char *path,mode_t mode);
  path:创建有名管道的全路径名
  mod:创建有名管道的模式,指存取权限
  dev:设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到
  注意:有名管道创建后就可以使用了,有名管道和管道的使用方法基本是相同的。只是使用有名管道的时候必须先调用open()将其打开
  因为有名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件
 
3. 消息队列、信号量和共享内存
   函数 msgget semget shmget  msgctl semctl shmctl
   消息队列、信号量和共享内存都是ipdc资源

   每个ipc资源有两个唯一的标志与其相连 关键字和标识(关键字类似于文件名,标志类似于文件描述字用来访问资源的操作)

   关键字获得函数 
   #include<sys/types.h>
   #include<sys/ipc.h>
   key_t ftok(const char * pathname,int proj_id);
  
   ipc_perm{              //结构,当进程创建ipc资源时,内核存储了ipc资源的属主和组id和访问权限等信息。
     uid_t uid            //当前有效用户id
     gid_t gid            //当前有效组id
     cuid_t cuid          //创建用户id
     cgid_t cgid          //创建组id
     mode_t mode          //访问权限
   };
  
   ipc资源创建之后,可以长期存储在文件系统中,直到被删除或者系统重启。
   shell命令:ipcs -q -s -m   ipcrm
       
   A、消息队列
   struct msqid_ds {            //消息队列相连的数据结构
     struct ipc_perm msg_perm;  //该消息队列的属主和访问权限
     struct msg*    msg_first;  //指向第一个消息的指针
     struct msg*     msg_last;  //指向最后一个消息的指针
     msgqnum_t      msg_qnum;   //当前消息的个数
     msglen_t        msg_qytes; //最大容量字节数
     pid_t           msg_lspid; //最后一个调用msgsnd的进程
     pid_t           msg_lrpid; //最后一个调用msgrcv的进程
     time_t          msg_stime; //最后发送时间
     time_t          msg_rtime; //最后接收时间
     time_t          msg_ctime; //最后修改时间
   }
  
   struct msg {
    struct msg* mas_next; 下个消息的指针
    long msg_type;        消息的类型
    caddr_t msg_sport;    消息正文地址
    sort msg_ts;          消息正文的大小
   }
  
   int msgget(key_t key, int msgflg);
   key:为ftok函数的返回值
   msgflag:标志参数,可取值为IPC_CREATE,IPC_EXCL
   IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
   IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
  
   int msgctl(int msgid, int cmd, struct msqid_ds* buf);
   控制消息队列cmd
   IPC_STAT   获得msgid的消息队列数据结果到buf中
   IPC_SET    设置buf中ipc_perm为消息队列的新值
   IPC_RMID   删除消息队列
  
   int msgsnd(int msgid, const void* msg, size_t size, int msgflag);
   mysgid:消息队列标识符
   msg:指向发送的消息
   size:要发送消息的大小,不包含消息类型占用的4个字节
   msgflag:操作标志位,可以设置为0或者IPC_NOWAIT
   0:当消息队列已满的时候,msgsnd将会阻塞,直到消息可以写进消息队列
   IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回。
   msgsnd()为阻塞函数,当消息队列容量满或者消息个数满会阻塞,如果消息队列被删除,则EIDRM错误,被信号中断E_INTR。
              如果设置IPC_NOWAIT会返回-1并且置EAGAIN错误

   ssize_t msgrcv(int msgid, void* msg, size_t size, long int msgtyp, int msgflag);
   size实际上是指正文段的大小,总大小应该是size+4字节,接收是同样的意思
   msgtyp:请求读取的消息类型。
   msgflag: 操作标志位,可以为IPC_NOWAIT,IPC_EXCEPT,IPC_NOERROR
   IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG;
   IPC_EXCEPT:与msgtyp配合使用返回队列中第一个类型不为msgtyp的消息
   IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将被丢弃
   long int msgtyp  if == 0 接收第一个消息           
                    if > 0  接收类型等于msgtyp的第一个消息
                    if < 0  接收类型等于或者小于msgtyp绝对值的第一个消息


                           
  B、信号量
  struct semid_ds {
   struct ipc_perm sem_perm;       包含信号量资源的属主和访问权限
   struct sem      *sem_base;      指向信号量集合的指针
   unsigned short  int sem_nsems;  集合中信号的个数
   time_t          sem_otime;      最后一次操作的时间
   time_t          sem_ctime;      最后一次修改的时间
  }
 
  struct sem {
  unsigned short semval;           当前信号量值 
  pid_t     sempid;                最后修改信号量的进程
  unsignde short semcnt;           等待进行p操作的进程数
  unsigned short semzcnt;          等待semval为0的进程数
  }
 
  int semget(key_t key, int semnum, int flag)
  key:ftok函数的返回值
  semnum:要创建的信号集包含的信号个数,如果只是打开信号集,其值为0即可
  flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
   IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
   IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
 
  信号量操作
  int semop(int semid, struct sembuf* buf, int bufsize);
  semid:信号集的标志符
  buf:指向进行操作的结构体数组首地址
  bufsize:指出将要进行操作的信号的个数

  struct sembuf {
    short semnum; 信号量集合中的信号量编号
    short val;    val>0进行v操作加val,表示进程释放控制的资源;
                  val<0进行p操作减val,若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN 
                  val=0时阻塞等待信号量为0;若没有设置IPC_NOWAIT,调用进程进入睡眠状态,直到信号值为0;否则进程不会睡眠,直接返回EAGAIN
    short flag;  IPC_NOWAIT IPC_UNDO当执行pv操作之后,进程突然终止没有进行相应的vp操作
  }buf; 

  semctl(int semid, int semnum, int cmd, union semun arg) 可以为信号量初始值,删除信号量设置权限等
  semid:信号集标志符
  semnum: 为信号量集合中的某个信号量操作
  cmd:    SETVAL,IPC_STAT,IPC_SET,SETALL,GETALL,IPC_INFO,IPC_RMID
  union semun {
   short val;              设置信号量的值SETVAL
   struct semid_ds* buf;   设置或者获得semid_ds结构IPC_STAT,IPC_SET
   unsigned short* array;  指向信号量的数组,用于集体初始化SETALL,GETALL
   struct seminfo *buf;    为控制IPC_INFO提供的缓存
  } arg;
  IPC_SET:对信号集的属性进行设置
  IPC_RMID:把semid指定的信号集从系统中删除
  GETPID:返回最后一个执行semop操作的进程的ID
  GETVAL:返回信号集中semnum指定信号的值
  GETALL:返回信号集中所有信号的值
  GETNCNT:返回正在等待资源的进程的数量
  GETZCNT:返回正在等待完全空闲资源的进程数量
  SETVAL:设置信号集中semnum指定的信号的值
  SETALL:设置信号集中所有信号的值
 
  C、共享存储
  shmget(key_t key, int size, int flag)
  key:ftok返回的值
  size:以字节为单位指定内存的大小
  flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
   IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
   IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)

  size为n时表示获得或者创建这么大的共享段
  size为0时表示直接获得整个共享段
  size大于创建的共享段时,返回错误
  (创建时指定n, 获得时一般直接为 0)
  struct shmid_ds {
    struct ipc_perm shm_perm;
    size_t shm_segsz     大小
    pid_t shm_pid        最后操作的进程id
    pid_t shm_cpid       创建的进程id
    shmatt_t shm_nattch  当前连接数
    time_t shm_atime     最后调用shmat的时间
    time_t shm_dtime     最后调用shmdt的时间
    time_t shm_ctime     最后调用shmctl的时间
  }
  void* shmat(int shmid, const void* shmaddr, int flag);
  shmid:共享内存区的标志符
  shmaddr一般为0,内核自动为程序一个连接到共享内存段的地址返回给进程;共享内存的附加点
  flag  SHM_RDONLY SHM_RND(当shmaddr设置为自己的值时,shm_rnd标志设置后,将会对这个地址做适当的截断以适合系统的对齐)
  参数shmaddr不同取值的含义如下:
    如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给flag参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定
    的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址(SHMLBA,一个常址)  
    通常将参数shmaddr设置为NULL

  int shmdt(const void* shmaddr);  //断开与共享内存区的联系
  shmaddr:shmat的返回值
  只有当shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除,一般来说,当一个进程终止时,他所附加的共享内存区都会自动脱离

  shmctl(int shmid, int cmd, struct shmid_d* buf);
  shmid:共享内存区的标志符
  cmd:IPC_STAT IPC_SET IPC_RMID SHM_LOCK SHM_UNLOCKroot用户专用
  buf:指向shmid_ds结构体的指针