嵌入式之路,贵在日常点滴
---阿杰在线送代码
目录
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。--》面试可能会问
以Linux中的C语言编程为例。
管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。
1、特点:
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
2、原型:
- #include
- int pipe(int fd[2]); // 返回值:若成功返回0,失败返回-1
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:

要关闭管道只需将这两个文件描述符关闭即可。
3、例子
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:

若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。
- #include "stdio.h"
- #include
- #include "string.h"
- #include
-
- int main()
- {
- int fd[2];
- int pid;
- char buf[128];
-
- //int pipe(int pipefd[2]);
- if(pipe(fd) == -1){
- printf("creat pipe failed\n");
- }
-
- pid = fork();//创建子进程
-
- if(pid<0){
- printf("creat child failed\n");
- }
- else if(pid > 0){
- printf("this is father\n");
- close(fd[0]);
- write(fd[1],"hello from father",strlen("hello from father"));
- wait(NULL);//不能让父进程先结束,让父进程在这边等着
- }
- else{
- printf("this is child\n");
- close(fd[1]);
- read(fd[0],buf,128);//把fd[0]端的数据读到buf里面,如果fd[0]里面没数据,read就会阻塞
- printf("read from father:%s\n",buf);
- exit(0);
- }
-
- return 0;
- }
运行结果:

当程序运行到子进程处,readbuf里面数据为空,这时read堵塞,父进程往fd[1]里面写数据,写入之后,父进程先不结束而是等待子进程运行之后再结束。 如何体现呢,我们把上面的代码进行个微改,在父进程加个睡眠3s
- #include "stdio.h"
- #include
- #include "string.h"
- #include
-
- int main()
- {
- int fd[2];
- int pid;
- char buf[128];
-
- //int pipe(int pipefd[2]);
- if(pipe(fd) == -1){
- printf("creat pipe failed\n");
- }
-
- pid = fork();
-
- if(pid<0){
- printf("creat child failed\n");
- }
- else if(pid > 0){
- sleep(3);
- printf("this is father\n");
- close(fd[0]);
- write(fd[1],"hello from father",strlen("hello from father"));
- wait(NULL);
- }
- else{
- printf("this is child\n");
- close(fd[1]);
- read(fd[0],buf,128);
- printf("read from father:%s\n",buf);
- exit(0);
- }
-
- return 0;
- }
运行结果:

FIFO,也称为命名管道,它是一种文件类型。
1、特点
FIFO可以在无关的进程之间交换数据,与无名管道不同。
FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
2、原型
- #include
- 返回值:成功返回0,出错返回-1
-
- int mkfifo(const char *pathname, mode_t mode);
- char *pathname:管道名字,mode:管道权限,0600可读可写
其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- if((mkfifo("./file",0600) == -1) && errno != EEXIST)//如果管道创建失败且失败的原因不是已存在
- {
- printf("make fifo failed\n");
- perror("why");
- }
-
- return 0;
- }
当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- char buf[30] = {0};
- int nread = 0;
-
- //如果管道创建失败且失败的原因不是已存在
- if((mkfifo("./file",0600) == -1) && errno != EEXIST)
- {
- printf("make fifo failed\n");
- perror("why");
- }
-
- int fd = open("./file",O_RDONLY);
- printf("open success\n");
-
- while(1){
- nread = read(fd,buf,30);
-
- printf("read %d byte from fifo,context:%s\n",nread,buf);
- }
-
- return 0;
- }
-
- 若没有指定(默认),只读open到阻塞到其他进程为写而打开次fifo,
- 只写open要阻塞到其他进程为读而打开次fifo。
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- int cnt = 0;
- char *str = "message from fifo";
-
- int fd = open("./file",O_WRONLY);
- printf("write open success\n");
-
- while(1){
- write(fd,str,strlen(str));
- sleep(1);
- if(cnt == 5){
- break;
- }
- }
- close(fd);
- return 0;
- }
两者即时通信才能读写成功,两个进程间通信用fifo实现
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
1、特点
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
2、原型
- #include
-
- 创建或打开消息队列:成功返回队列ID,失败返回-1
- int msgget(key_t key, int flag);
- key 队列索引值,通过索引值找到队列;flag打开队列的方式
-
-
- 添加消息:成功返回0,失败返回-1
- int msgsnd(int msqid, const void *ptr, size_t size, int flag);
- msgid:由msgget函数返回的消息队列标识码
- *ptr:指针指向准备发送的消息
- size:指向的消息的长度(不包括消息类型的long int长整型)
- flag:默认为0
-
- 读取消息:成功返回消息数据的长度,失败返回-1
- int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
-
- 控制消息队列:成功返回0,失败返回-1
- int msgctl(int msqid, int cmd, struct msqid_ds *buf);//
在以下两种情况下,msgget将创建一个新的消息队列:
IPC_CREAT标志位。IPC_PRIVATE。函数msgrcv在读取消息队列时,type参数有下面几种情况:
type == 0,返回队列中的第一个消息;type > 0,返回队列中消息类型为 type 的第一个消息;type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。(其他的参数解释,请自行Google之)
3、原理
发送消息:
- #include "stdio.h"
- #include
- #include
- #include
- #include "string.h"
-
- //int msgget(key_t key, int msgflg);
- //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
- // int msgflg);
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[128]; /* message data */
- };
-
-
- int main()
- {
- struct msgbuf sendBuf = {888,"this is message from quen"};
- struct msgbuf readBuf;
-
- 1、获取
- int msgId = msgget(0x1234,IPC_CREAT|0777);
- //如果有该队列则直接打开,没有则创建 0777:权限 可读可写可执行
- if(msgId == -1){
- printf("get que failuer\n");
- }
-
- 2、既发送
- msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
- printf("send over\n");
-
- 3、又接收
- msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
- printf("reaturn from get:%s\n",readBuf.mtext);
-
-
- return 0;
- }
读取消息:
- #include "stdio.h"
- #include
- #include
- #include
- #include "string.h"
-
- //int msgget(key_t key, int msgflg);
- //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
- // int msgflg);
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[128]; /* message data */
- };
-
-
- int main()
- {
- struct msgbuf readBuf;
-
- 1、获取队列
- int msgId = msgget(0x1234,IPC_CREAT|0777);
- if(msgId == -1){
- printf("get que failuer\n");
- }
-
- 2、接收
- msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
- //没有读到数据时 会一直阻塞在这
- printf("read from que:%s\n",readBuf.mtext);
-
- 3、发送
- struct msgbuf sendBuf = {988,"thank you for reach"};
- msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
-
- return 0;
- }
运行结果:


系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
头文件
#include <sys/types.h>
#include
函数原型:
key_t ftok( const char * fname, int id )
fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:
key_t key;
key = ftok(".", 1); 这样就是将fname设为当前目录。
id是子序号。虽然是int类型,但是只使用8bits(1-255)。
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i
- #include "stdio.h"
- #include
- #include
- #include
- #include "string.h"
-
- //int msgget(key_t key, int msgflg);
- //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
- // int msgflg);
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[128]; /* message data */
- };
-
-
- int main()
- struct msgbuf readBuf;
-
- key_t key;
- key = ftok(".",'1');
- printf("key=%x\n",key);
-
- int msgId = msgget(key,IPC_CREAT|0777);
- if(msgId == -1){
- printf("get que failuer\n");
- }
-
- msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
- printf("read from que:%s\n",readBuf.mtext);
-
- struct msgbuf sendBuf = {988,"thank you for reach"};
- msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
-
- msgctl(msgId,IPC_RMID,NULL);//用完队列把队列去除
-
- return 0;
- }
- #include "stdio.h"
- #include
- #include
- #include
- #include "string.h"
-
- //int msgget(key_t key, int msgflg);
- //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
- // int msgflg);
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[128]; /* message data */
- };
-
-
- int main()
- {
- struct msgbuf sendBuf = {888,"this is message from quen"};
- struct msgbuf readBuf;
-
- key_t key;
- key = ftok(".",'1');
- printf("key=%x\n",key);
-
- int msgId = msgget(key,IPC_CREAT|0777);
- if(msgId == -1){
- printf("get que failuer\n");
- }
-
- msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
- printf("send over\n");
-
- msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
- printf("reaturn from get:%s\n",readBuf.mtext);
-
- msgctl(msgId,IPC_RMID,NULL);
-
- return 0;
- }
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
1、特点
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
2、原型
- #include
-
- // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
- int shmget(key_t key, size_t size, int flag);
- (1)生成键值
- (2)开辟的大小必须以M为基本单位
- (3)共享内存权限
-
- // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
- void *shmat(int shm_id, const void *addr, int flag);
- (1)共享内存的ID
- (2)一般写0,让系统自动分配共享内存的地址
- (3)一般写0,内存可读可写
-
- // 断开与共享内存的连接:成功返回0,失败返回-1
- int shmdt(void *addr);
- (1)共享内存连接成功后返回的指针
-
- // 控制共享内存的相关信息:成功返回0,失败返回-1
- int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
- (1)共享内存ID
- (2)IPC_RMID,一般写这个
- (3)不关心这个写0
当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。
当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。
shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。
shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。
3、编程实现步骤
- #include
- #include
- #include "stdio.h"
- #include "stdlib.h"
- #include
- #include
-
- //int shmget(key_t key, size_t size, int shmflg);
- int main()
- {
- int shmid;
- char *shmaddr;
-
- key_t key;
- key = ftok(".",1);
-
- /*共享内存的创建或者打开*/
- shmid = shmget(key,1024*4,IPC_CREAT|0666);//共享内存的大小是以兆对齐的
- if(shmid == -1){
- printf("shmget error\n");
- exit(-1);
- }
-
- /*将共享内存映射到要通信的进程*/
- shmaddr = shmat(shmid,0,0);//0代表默认方式
-
- printf("shamt success\n");
-
- /*往共享内存中写入数据*/
- strcpy(shmaddr,"a jie zai xian song dai ma");
-
- /*延迟5s 释放共享内存 然后 删除共享内存*/
- sleep(5);//延时5秒为使从共享内存读数据有缓冲时间
- shmdt(shmaddr);
- shmctl(shmid,IPC_RMID,0);
-
- printf("quit\n");
-
- return 0;
- }
- #include
- #include
- #include "stdio.h"
- #include "stdlib.h"
- #include
- #include
-
- //int shmget(key_t key, size_t size, int shmflg);
- int main()
- {
- int shmid;
- char *shmaddr;
-
- key_t key;
- key = ftok(".",1);
-
- /*获取共享内存*/
- shmid = shmget(key,1024*4,0);
- if(shmid == -1){
- printf("shmget error\n");
- exit(-1);
- }
-
- /*挂载共享内存*/
- shmaddr = shmat(shmid,0,0);
-
- printf("shamt success\n");
- /*读取共享内存数据*/
- printf("data: %s\n",shmaddr);
-
- /*释放共享内存*/
- shmdt(shmaddr);
-
- //因为写已经删除共享内存,所以这边不需要删除了
- printf("quit\n");
-
- return 0;
- }
运行结果:


从共享内存读数据时,必须要在这延时的5秒(有一个延迟)内进行,否则共享内存将被释放,无法与共享内存建立联系。
- ipcs -m 查看共享内存的ID值和大小
- ipcrm -m shmid 移除未释放的共享内存

对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号的名字和编号:
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。

信号的处理:信号的处理有三种方法,分别是:忽略、捕捉和默认动作
SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景man 7 signal来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。也可以参考 《UNIX 环境高级编程(第三部)》的 P251——P256中间对于每个信号有详细的说明。忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
执行缺省操作:Linux对每种信号都规定了默认操作
了解了信号的概述,那么,信号是如何来使用呢?
其实对于常用的 kill 命令就是一个发送信号的工具,kill 9 PID来杀死进程。比如,我在后台运行了一个 top 工具,通过 ps 命令可以查看他的 PID,通过 kill 9 来发送了一个终止进程的信号来结束了 top 进程。如果查看信号编号和名称,可以发现9对应的是 9) SIGKILL,正是杀死该进程的信号。而以下的执行过程实际也就是执行了9号信号的默认动作——杀死进程。

对于信号来说,最大的意义不是为了杀死信号,而是实现一些异步通讯的手段,那么如何来自定义信号的处理函数呢?
信号处理函数的注册不只一种方法,分为入门版和高级版
signalsigaction信号发送函数也不止一个,同样分为入门版和高级版
1.入门版:kill
2.高级版:sigqueue
sighandler_t signal(int signum, sighandler_t handler)
第一个参数signum:表示要捕捉哪个信号。
第二个参数handler:函数指针,当捕捉到这个信号时,执行信号处理函数
(1)捕捉信号
调用信号:
- #include "stdio.h"
- #include
-
- //typedef void (*sighandler_t)(int);
- //sighandler_t signal(int signum, sighandler_t handler);
-
- void handler(int signum)
- {
- printf("get signum=%d\n",signum);
- printf("never quit\n");
- }
-
- int main()
- {
- signal(SIGINT,handler);
-
- while(1);
- return 0;
- }
发送信号;
- #include "stdio.h"
- #include
- #include
-
- int main(int argc,char **argv)
- {
- int signum;
- int pid;
-
- signum = atoi(argv[1]);
- pid = atoi(argv[2]);
-
- printf("signum=%d,pid=%d",signum,pid);
-
- kill(pid,signum);
- printf("send signal ok");
-
- return 0;
- }
运行结果


(2)忽略信号
ps -aux|grep -xinhao:查看后缀为xinhao的信号,用kill -9 信号号来杀死信号
- #include "stdio.h"
- #include
-
- //typedef void (*sighandler_t)(int);
- //sighandler_t signal(int signum, sighandler_t handler);
-
- void handler(int signum)
- {
- printf("get signum=%d\n",signum);
- printf("never quit\n");
- }
-
- int main()
- {
- signal(SIGINT,SIG_IGN);
-
- while(1);
- return 0;
- }

(3)高级信号编程
在上面的信号编程入门版中,实现了信号的发送。高级版则在信号发送过程中携带了消息。
信号如何携带消息
思路
发信号
1.用什么发 sigqueue()
2.怎么放入消息
收信号
1.用什么绑定函数(收到信号如何让处理)sigaction()
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact(备份原操作));
2.如何读出消息
int sigqueue(pid_t pid, int sig, const union sigval value);
第一个参数 发给谁
第二个参数 发的是啥
第三个参数 要发的消息
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
(1)第一个参数表示接受的信号。
(2)第二个参数表示收到这个信号想干嘛
(3)第三个参数表示备份,通常NULL
- //接收信号
- #include
- #include
-
- void handler (int signum, siginfo_t *info, void *context)
- {
- printf("get signum = %d\n",signum);
-
- if(context != NULL){
- printf("get data = %d\n",info->si_int);//这里是指针所以要用->
- printf("get data = %d\n",info->si_value.sival_int);
- printf("from %d\n",info->si_pid);//发送者的pid
- }
-
- }
-
- int main()
- {
- struct sigaction act;
- printf("pid = %d\n",getpid());
- act.sa_flags = SA_SIGINFO;//要收信号就把这个参数设置为SA_SIGINFO
- act.sa_sigaction = handler;//收到信号处理该函数
- sigaction(SIGUSR1,&act,NULL);//接收SIGUSR1信号,SIGUSR1代号是10
-
-
-
- while(1);
- return 0;
- }
- //发送信号
- #include
- #include
-
- // int sigqueue(pid_t pid, int sig, const union sigval value);
-
- int main(int argc,char **argv)
- {
- int signum;
- int pid;
-
- signum = atoi(argv[1]);//信号代号,SIGUSR1代号是10
- pid = atoi(argv[2]);//要发送到的进程号
-
- union sigval value;
- value.sival_int = 100;//写入要发送的数据
-
-
-
- sigqueue(pid,signum,value);
- printf("%d done\n",getpid());
-
-
- return 0;
- }
运行结果:

多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1、特点
信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
支持信号量组。
2、原型
最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。
Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
#include
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);
// 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);
当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 。
在semop函数中,sembuf结构的定义如下:
struct sembuf
{
short sem_num; // 信号量组中对应的序号,0~sem_nums-1
short sem_op; // 信号量值在一次操作中的改变量
short sem_flg; // IPC_NOWAIT, SEM_UNDO
}
- #include
- #include
- #include
- #include
- #include
-
- //联合体,用于semctl初始化
- union semun {
- int val; /* Value for SETVAL */
- struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
- unsigned short *array; /* Array for GETALL, SETALL */
- struct seminfo *__buf; /* Buffer for IPC_INFO
- (Linux-specific) */
- };
-
- /*放锁函数*/
- /*释放资源并将信号量值+1*/
- /*如果有进程正在挂起等待,则唤醒它们*/
- void vreturnKey(int id)
- {
- struct sembuf set;
-
- set.sem_num = 0;/*序号*/
- set.sem_op = 1;/*v操作*/ 临界资源:锁+1
- set.sem_flg = SEM_UNDO;
-
- semop(id,&set,1);//1代表只操作了一个
-
- printf("returnkey\n");
-
- }
- /*拿锁函数*/
- /*若信号量值为1,获取资源并将信号量值-1*/
- /*若信号量值为0,进程挂起等待*/
- void pGetKey(int id)
- {
- struct sembuf set;
-
- set.sem_num = 0;/*序号*/
- set.sem_op = -1;/*v操作*/ 临界资源:锁-1
- set.sem_flg = SEM_UNDO;
-
- semop(id,&set,1);//1代表只操作了一个
-
- printf("getkey\n");
-
- }
-
- int main ()
- {
- int key;
- int semId;
- int pid;
- key = ftok(".",1);
-
- /*创建或者创建一个信号量组,参数1代表信号量集合中有一个信号量*/
- semId = semget(key,1,IPC_CREAT|0666);
-
- union semun initsem;
- initsem.val = 0;//锁被子进程拿走(初始值:初值设为0资源被占用)
-
- /*初始化信号量,参数0代表操作第0个信号量,SETVAL设置信号量的值,设置为initsem*/
- semctl(semId,0,SETVAL,initsem);
-
- /*创建子进程*/
- pid = fork();
-
- if(pid >0)
- {
- pGetKey(semId);/*拿锁*/
-
- printf("this is father process\n");
-
- vreturnKey(semId);/*锁放回去*/
- semctl(semId,0,IPC_RMID);//销毁锁
-
- }
- else if(pid == 0)
- {
- printf("this is child process\n");
-
- vreturnKey(semId);//放回锁
- }
- else
- {
- printf("no child process is created!\n");
-
- }
-
- return 0;
- }
运行结果

1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存