Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1178531
  • 博文数量: 181
  • 博客积分: 4968
  • 博客等级: 上校
  • 技术积分: 1867
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-13 21:52
文章分类

全部博文(181)

文章存档

2015年(2)

2013年(6)

2012年(22)

2011年(41)

2010年(27)

2009年(51)

2008年(32)

我的朋友

分类: LINUX

2009-02-13 15:27:32

Linux进程间通信

linux下进程间通信的几种主要手段简介:

  1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
  2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
  3. 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  4. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
  5. 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
  6. 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
-------------------------------------------------------------------------------
1. 管道。
  匿名一次性使用的,半双工。一个进程往输出端写管道,另一个进程从输入端读管道。
  #include
  int pipe(int fd[2]);
  fd[0]:表示读端
  fd[1]:表示写端
2. FIFO
  有名管道,作为特别文件存储于文件系统中。有名管道一旦建立就存在于文件系统中,除非显示的unlink
  #include
  #include
  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
   #include
   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结构体的指针
阅读(1087) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~