写给大忙人读的进程间通信(附进程同步源码)

摘要:


        进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。另外,系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。此外,还有双方都可以访问的外设。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。

 

简介:


       进程间通信(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)或调试程序的停止执行信号;

 

管道:


       管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。

       管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。

 

系统V IPC机制:


         常用的三种进程间通信机制(IPC)机制:消息队列、信号灯和共享内存(message queues,semaphores and shared memory

 

Message Queues(消息队列)

        消息队列就是消息的一个链表,它允许一个或多个进程向它写消息,一个或多个进程从中读消息。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

你可能感兴趣的:(工作前的节奏)