进程通信-Linux

来源:互联网 发布:精通c语言能做什么知乎 编辑:程序博客网 时间:2024/06/15 07:42

参考博客:https://yq.aliyun.com/articles/47679  Linux进程间通信的几种方式总结

http://blog.csdn.net/bresponse/article/details/7085774  进程间通信方式总结(windows 和linux)

http://blog.csdn.net/tax10240809163com/article/details/50854123  进程间通信的几种方法

Linux进程的几种通信方式   IPC(Inter-Process Communication,进程间通信)

1、管道(pipe),流管道(s_pipe)和有名管道(FIFO)
2、信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个时间已经发生。
3、消息队列
4、共享内存:映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问.最快的IPC
5、信号量:信号量是一个计数器,用来控制多个进程对共享资源的访问,它常作为一种锁机制,作为进程/线程同步的手段
6、套接字(socket)
各种通信方式的比较和优缺点:
pipe管道:单向数据流,只有父子进程能通讯,度慢,容量有限;FIFO:任何进程间都能通讯,但速度慢,其长期存在于系统 ,易出错
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
信号量:不能传递复杂消息,只能用来同步
共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
1、管道:

1.1、匿名管道:调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端。管道作用于有血缘关系的进程之间,通过fork来传递。pipe函数调用成功返回0,调用失败返回-1

 pipe管道的局限性:(1)只支持单向数据流;(2)没有名字,只能用于拥有亲缘关系的进程之间,

(3)管道的缓冲区是有限的(分配一个页面大小)(4)管道所传送的是无格式,读写双方必须实现约定好数据格式。



1.2、有名管道:(用法类似于读写文件):创建一个有名管道,解决无血缘关系的进程通信, fifo:

zxh@ubuntu:~$ mkfifo myfifo  //命令

mkfifo既有命令也有函数

#include <sys/types.h> 
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

2、消息队列点击打开链接 //使用消息队列

    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
命名管道VS消息队列,消息队列的优势:(1)消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。(2)同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。(3)接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。

2.1、msgget函数
该函数用来创建和访问一个消息队列。它的原型为:
  1. int msgget(key_t, key, int msgflg);  
2.2、msgsnd函数
该函数用来把消息添加到消息队列中。它的原型为:
  1. int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);  //msgid是由msgget()函数返回的消息队列标识符。
2.3、msgrcv函数
该函数用来从一个消息队列获取消息,它的原型为
  1. int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);  
msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。
2.4、msgctl函数
该函数用来控制消息队列,它与共享内存的shmctl函数相似,它的原型为:
  1. int msgctl(int msgid, int command, struct msgid_ds *buf);  
command是将要采取的动作,它可以取3个值,
    IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
    IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
    IPC_RMID:删除消息队列
3.共享内存  点击打开链接  //使用共享内存,共享内存并未提供同步机制.
    共享内存就是允许两个不相关的进程访问同一个内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中.
3.1、shmget函数--创建共享内存
  1. int shmget(key_t key, size_t size, int shmflg);  //参数:
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
第二个参数,size以字节为单位指定需要共享的内存容量。第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,
3.2、shmat函数--启动对该共享内存的访问,把共享内存连接到当前进程的地址空间
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);  
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
return:调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.
3.3、shmdt函数--共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用
该函数用于将共享内存从当前进程中分离。注意,将。它的原型如下:
  1. int shmdt(const void *shmaddr);  
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

3.4、shmctl函数---控制共享内存,如信号量的semctl函数
  1. int shmctl(int shm_id, int command, struct shmid_ds *buf);  
第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
  1. struct shmid_ds  
  2. {  
  3.     uid_t shm_perm.uid;  
  4.     uid_t shm_perm.gid;  
  5.     mode_t shm_perm.mode;  
  6. };  
4.信号量       点击打开链接   //算是进程同步,只是一个标志,
防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量)-1)和发送(即V(信号变量)+1)信息操作。
4.1、semget函数--创建一个新信号量或取得一个已有信号量
  1. int semget(key_t key, int num_sems, int sem_flags);  
第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。
第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
return:semget函数成功返回一个相应信号标识符(非零),失败返回-1.
4.2、semop函数--改变信号量的值
  1. int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  
sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:
  1. struct sembuf{  
  2.     short sem_num;//除非使用一组信号量,否则它为0  
  3.     short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,  
  4.                     //一个是+1,即V(发送信号)操作。  
  5.     short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
  6.                     //并在进程没有释放该信号量而终止时,操作系统释放信号量  
  7. };  
4.3、semctl函数--直接控制信号量信息
  1. int semctl(int sem_id, int sem_num, int command, ...);  
如果有第四个参数,它通常是一个union semum结构,定义如下:
  1. union semun{  
  2.     int val;  
  3.     struct semid_ds *buf;  
  4.     unsigned short *arry;  
  5. };  
前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
信号量小结:信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。