进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。另外,系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。此外,还有双方都可以访问的外设。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。
进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。
进程间通信主要包括管道, 系统IPC(包括消息队列,信号,共享存储), 套接字(SOCKET)。系统IPC的三种方式类同,都是使用了内核里的标识符来识别.
进程间通信的目的:
1)数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
2)共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
3)通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4)资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
5)进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
进程通过与内核及其它进程之间的互相通信来协调它们的行为。Linux支持多种进程间通信(IPC)机制,信号和管道是其中的两种。除此之外,Linux还支持System V 的IPC机制(用首次出现的Unix版本命名)
在一个信号的生命周期中有两个阶段:生成和传送。当一个事件发生时,需要通知一个进程,这时生成一个信号。当进程识别出信号的到来,就采取适当的动作来传送或处理信号。在信号到来和进程对信号进行处理之间,信号在进程上挂起
几种常见的信号:
SIGHUP: 从终端上发出的结束信号;
SIGINT: 来自键盘的中断信号(Ctrl-C);
SIGQUIT:来自键盘的退出信号(Ctrl-\);
SIGFPE: 浮点异常信号(例如浮点运算溢出);
SIGKILL:该信号结束接收信号的进程;
SIGALRM:进程的定时器到期时,发送该信号;
SIGTERM:kill 命令发出的信号;
SIGCHLD:标识子进程停止或结束的信号;
SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号;
管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。
管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。
常用的三种进程间通信机制(IPC)机制:消息队列、信号灯和共享内存(message queues,semaphores and shared memory
消息队列就是消息的一个链表,它允许一个或多个进程向它写消息,一个或多个进程从中读消息。Linux维护了一个消息队列向量表:msgque,来表示系统中所有的消息队列。
从队列中读消息与向队列中写消息是一个相似的过程。进程对消息队列的访问权限一样要被检查。读进程可以选择从队列中读取第一条消息而不管消息的类型,也可以选择从队列中读取特殊类型的消息。如果没有符合条件的消息,读进程会被加到消息队列的读等待队列,然后运行调度程序。当一个新的消息写到消息队列时,这个进程会被唤醒,继续它的运行。
Linux提供了四个消息队列操作:
1、创建或获得消息队列(MSGGET):int sys_msgget (key_t key, int msgflg)
2、发送消息:int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
3、接收消息:int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
4、消息控制:int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
消息队列和管道提供相似的服务,但消息队列要更加强大并解决了管道中所存在的一些问题。消息队列传递的消息是不连续的、有格式的信息,给对它们的处理带来了很大的灵活性。可以用不同的方式解释消息的类型域,如可以将消息的类型同消息的优先级联系起来,类型域也可以用来指定接收者。
小消息的传送效率很高,但大消息的传送性能则较差。因为消息传送的过程中要经过从用户空间到内核空间,再从内核空间到用户空间的拷贝,所以,大消息的传送其性能较差。另外,消息队列不支持广播,而且内核不知道消息的接收者
在进行间同步的方式汇总,共享内存速度最快。常见的操作:
1、int shmget(key_t key, int size, int flag); /* 获得一个共享存储标识符*/
2、void *shmat(int shmid, void *addr, int flag); /* 将共享内存连接到自身地址空间中*/
如果一个进程通过fork创建了子进程,则子进程继承父进程的共享内存,既而可以直接对共享内存使用,不过子进程可以自身脱离共享内存。
进程间通信各种方式效率比较:
类型 |
无连接 |
可靠 |
流控制 |
消息类型优先级 |
普通PIPE |
N |
Y |
Y |
N |
流PIPE |
N |
Y |
Y |
N |
命名PIPE(FIFO) |
N |
Y |
Y |
N |
消息队列 |
N |
Y |
Y |
Y |
信号量 |
N |
Y |
Y |
Y |
共享存储 |
N |
Y |
Y |
Y |
UNIX流SOCKET |
N |
Y |
Y |
N |
UNIX数据包SOCKET |
Y |
Y |
N |
N |
后附共享内存实现进程间通信的源码:
https://download.csdn.net/download/fengxianghui01/12421062