现在最常用的进程间通信的方式有:信号,信号量,消息队列,共享内存。
所谓进程通信,就是不同进程之间进行一些""接触"",这种接触有简单,也有复杂。机制不
同,复杂度也不一样。通信是一个广义上的意义,不仅仅指传递一些massege。
他们的使用方法是基本相同的,所以只要掌握了一种的使用方法,然后记住其他的使用方
法就可以了。
1. 信号
在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也可以用来做其它的事
情,但是我还不知道做什么。
信号和信号量是不同的,他们虽然都可用来实现同步和互斥,但前者是使用信号处理器来
进行的,后者是使用P,V操作来实现的。
使用信号要先知道有哪些信号,在Linux下有31个需要记住的通用信号,据说也是system
V中最常用的那些。这里略。
1. 1信号相关函数:
#include
int sigaction(int signo, const struct sigaction *act, struct sigaction
*oact);
该函数用来为进程安装信号处理器,struct sigaction数据是用来保存信号处理器的相
关信息。
#include
int sigemptyset(sigset_t *set);
将信号集合清空。
int sigfillset(sigset_t *set);
将信号集合设置成包含所有的信号。在对信号进行操作以前一定要对信号集进行初始化。
int sigaddset(sigset_t *set, int signo);
向信号集中加入signo对应的新信号。
int sigdelset(sigset_t *set, int signo);
从信号集中删除signo对应的一个信号。
int sigismember(const sigset_t *set, int signo);
判断某个信号是否在信号集中。返回1则在,0则不在。
#include
int sigprocmask(int how,const sigset_t *set, sigset_t *oset);
用来设置进程的信号屏蔽码。信号屏蔽码可以用来在某段时间内阻塞一些信号集中的信
号,如果信号不在信号集中,就不必讨论它,因为肯定不响应,是否能生成也不肯定,我
没有做过试验。
1.2我所理解的使用信号机制的方法:
使用信号,主要做的事情就是信号处理器的工作,这里面是你想做的事情。就像中断处理
函数一样。
在使用信号以前,首先要初始化信号集,只有在信号集里面的信号才会被考虑。
有两种方法可以初始化信号集,一种是设置空信号集,一种是将所有的信号都加到信号集
中。如果你自己想要的信号集不是这两种,可以在初始化了以后通过添加和删除信号进行
定制。
如果在进程执行的一段时间内不想对某些信号进行响应,则可以使用sigprocmask对当前
的信号集中的一些信号进行阻塞,稍后再执行。
当你将信号集设置完毕后,在让他工作之前需要安装信号处理器。安装信号处理器可以实
现这几个功能:
指定信号处理函数的入口;指定信号屏蔽集合;指定信号处理器的一些标志。所谓信号处
理器,就是指定了一些处理方法,关键在于安装信号处理器,这是使正确的信号进行正确
的处理关键。在安装的时候,一定要对特定的信号赋予正确的信号处理函数。
我不知道不同进程之间的信号处理器能否混用,但是像一个特定的进程中有多少个信号处
理器这样的问题是不能提的。因为信号处理器是一个概念,他针对的是信号,就是说如果
你指定了一个数据结构,用它来存储针对某个信号的处理信息,那么安装信号处理器就是
赋予这个数据结构一些相关信息,使用信号处理器就是用这个数据结构存储的信息来组织
一种机制当发生这个信号的时候会做一些你实现设置好的处理。但是如果区分不同进程中
对同一个信号的不同处理器?我想处理器可能只对核它所属的进程有关的信号进行响应,
但是如果是这样的话,那这是怎么实现的呢?
不过有一点是可以知道的,那就是每一个信号都有一个信号处理器(确定的),可以动过
安装信号处理器来指定她的行为。信号处理器由他自己的信息存储区域(我不知道在什么
地方),但是可以通过向sigaction类型的数据结构向信号处理器的信息存储区域中传递
信息。这个数据结构由一个就可以了,因为它只是临时传递数据的载体。
但是sigpromask和信号处理器里面的sigmask是不一样的,前者是在进程当前流程设置信
号屏蔽,后者是指定在信号处理器作用时需要屏蔽掉的信号。例如,在设置某个特定信号
的信号处理器时,我们当然不能让它的信号处理器工作了,因为还没有设置完吗,这是我
们可以使用sigprocmask来让当前的流程开始阻塞该信号,当设置完信号处理器以后,再
用sigprocmask恢复被阻塞的信号。而以后再接收到该信号时,信号处理器就可以工作了。
我的想法是,同一个信号在不同的进程里可以有不同的信号处理器(一般应该有一个缺省
处理),当系统中发生一个信号时,所有能接受到的进程都可以接收到这个信号,并用他
们自己的信号处理器对这个信号做出各自的响应。
1.3如何用信号来进行进程间的同步
同步的实现主要是通过在接受信号之前挂起进程,等待相关信号。所以涉及到异步信号安
全函数的概念。
不过信号如何来实现进程间的互斥,我理解不是很多,我想信号的主要用处还是在软中断
处理和进程同步。
2.信号量
信号量和信号是不同的东西,仔细想想就可以理解:信号是实现约定的固定的值,而信号
量是一个变量记录着某些特定信息。
信号量这种东西我们在操作系统课程中就已经接触过了,这里只是再草草说几句。信号量
分为有名和无名两种。进程间通信用有名信号量,同一进程内部通信一般用无名信号量。
这个我不再多说。
2.1信号量相关函数
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
创建一个新的信号量组或获取一个已经存在的信号量组。
#include
#include
#include
int semop(int semid, struct sembuf *sop, int nsops);
semop函数可以一次对一个或多个信号量进行操作。
Int semctl(int sem_id, int semnum, int cmd,/*union semun arg*/…);
该函数可以用来获取一些信号量的使用信息或者是来对信号量进行控制。
2.2我对信号量机制的理解
对信号量的操作只有两个:P, V。
为了在逻辑上便于组织信号量,信号量机制中有一个概念是信号量组。我们可以把一个信
号量组中创建相关的信号量,这样逻辑上清晰也便于管理。在使用之前你同样需要对他们
进行初始化:生成或打开信号量组,向其中生成或删除你指定的信号量。
对信号量的操作只用两种,他都是通过semop函数中的sops参数来指定的,如果这个参数
是一个数组的话,那么就是对多个信号量进行操作。Sops参数中的sem_op字段指明了对信
号量进行的是P操作还是V操作。你只要指定就行了,具体的操作不需要你去实现,函数中
都已经提供了。使用信号量,你得清楚信号量组id和信号量在信号量组中的位置(其实也
就是另一个id)。一个信号量必须属于一个信号量组,否则不能被系统所使用。切记!
信号量和信号量组是不会被系统所自动清理的,所以当你的进程退出前,千万别忘了清理
你生成的那些信号量们。
信号量既可以实现互斥,也可以实现同步,这里就不说了,操作系统课程中是有介绍的。
3.消息队列
消息队列是比较高级的一种进程间通信方法,因为它真的可以在进程间传送massege,你
传送一个""I seek you""都可以。
一个消息队列可以被多个进程所共享(IPC就是在这个基础上进行的);如果一个进程的
消息太多一个消息队列放不下,也可以用多于一个的消息队列(不过可能管理会比较复
杂)。共享消息队列的进程所发送的消息中除了massege本身外还有一个标志,这个标志
可以指明该消息将由哪个进程或者是哪类进程接受。每一个共享消息队列的进程针对这个
队列也有自己的标志,可以用来声明自己的身份。
对于系统中的每一个消息队列,都有一个数据结构来代表它,这个数据结构是msqid_ds,
这里略去不讲,在中可以看到它的原型。
3.1消息队列相关函数
使用消息队列之前,你要么获得这个消息队列,要么自己建立一个,否则是不能使用消息
队列的(我觉得这都像是多余的话,请见谅)。当这个消息队列不再使用时,也一定要有
一个进程来删除消息队列,系统是不会自动的清理消息队列和msgid_ds的。
Int msgget(key_t key, int msgflg);
获取一个存在的消息队列的ID,或者是根据跟定的权限创建一个消息队列。但是怎么样去
删除这个消息队列,我还不十分清楚。
Int msgctl(int msqid, int cmd, struct msqid_ds *buf);
用来从msqid_ds中获取很多消息队列本身的信息。
Int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg);
用于向队列发送消息。
Int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int
msgflg);
从队列中接收消息。
我这个文档里面对消息队列中的一些临界情况所述不多,因为这是我的小结,而非介绍。
在GNU C库技术中可以看到它的详细介绍。