码迷,mamicode.com
首页 > 系统相关 > 详细

进程间通信

时间:2015-02-28 18:14:51      阅读:282      评论:0      收藏:0      [点我收藏+]

标签:

现在在Linux 中使用较多的进程间通信方式主要有以下几种。
(1)管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
(2)信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知接受进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求
  效果上可以说是一样的。
(3)消息队列:消息队列是消息的链接表,包括Posix 消息队列systemV 消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以向消息队列中按照一定
  的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。
(4)共享内存:可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要
  依靠某种同步机制,如互斥锁和信号量等。
(5)信号量:主要作为进程间以及同一进程不同线程之间的同步手段。
(6)套接字(Socket):这是一种更为一般的进程间通信机制,它可用于不同机器之间的进程间通信,应用非常广泛。

1.管道通信

技术分享

通常先是创建一个管道,再通过fork()函数创建一子进程,该子进程会继承父进程所创建的管道。

 1 /*pipe_rw.c*/
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <errno.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <string.h>
 8 void main(void)
 9 {
10     int pipe_fd[2];
11     pid_t pid;
12     char buf_r[100];
13     char* p_wbuf;
14     int r_num;
15     memset(buf_r,0,sizeof(buf_r));
16     /*创建管道*/
17     if(pipe(pipe_fd)<0)
18     {
19         printf("pipe create error\n");
20     }
21     /*创建一子进程*/
22     if((pid=fork())==0)
23     {
24         printf("\n");
25         /*关闭子进程写描述符,并通过使父进程暂停2秒确保父进程已关闭相应的读描述符*/
26         close(pipe_fd[1]);
27         sleep(2);
28         /*子进程读取管道内容*/
29         if((r_num=read(pipe_fd[0],buf_r,100))>0)
30         {
31             printf("%d numbers read from the pipe is %s\n",r_num,buf_r);
32         }
33         /*关闭子进程读描述符*/
34         close(pipe_fd[0]);
35         exit(0);
36     }
37     else if(pid>0)
38     {
39         /*关闭父进程读描述符,并分两次向管道中写入Hello Pipe*/
40         close(pipe_fd[0]);
41         if(write(pipe_fd[1],"Hello",5)!= -1)
42             printf("parent write1 success!\n");
43         if(write(pipe_fd[1]," Pipe",5)!= -1)
44             printf("parent write2 success!\n");
45         /*关闭父进程写描述符*/
46         close(pipe_fd[1]);
47         sleep(3);
48         /*收集子进程退出信息*/
49         waitpid(pid,NULL,0);
50         exit(0);
51     }
52 }

技术分享

向管道中写入数据时,管道缓冲区一有空闲区域,如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。

 

标准流管道:

用popen 创建的管道必须使用标准I/O 函数进行操作,但不能使用前面的read、write一类不带缓冲的I/O 函数。与之相对应,关闭用popen创建的流管道必须使用函数pclose来关闭该管道流。

技术分享

 1 /*popen.c*/
 2 #include <stdio.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <fcntl.h>
 6 #define BUFSIZE 1000
 7 void main(void)
 8 {
 9     FILE *fp;
10     char *cmd = "ps -ef";
11     char buf[BUFSIZE];
12     /*调用popen函数执行相应的命令*/
13     if((fp=popen(cmd,"r"))==NULL)
14         perror("popen");
15     while((fgets(buf,BUFSIZE,fp))!=NULL)
16         printf("%s",buf);
17     pclose(fp);
18     exit(0);
19 }

 

FIFO :

前面的管道是无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。有名管道可以使互不相关的两个进程实现彼此通信。该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。不过值得注意的是,FIFO 是严格地遵循先进先出规则的,对管道及FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如lseek()等文件定位操作。在创建管道成功之后,就可以使用open、read、write这些函数了。

技术分享

技术分享

技术分享

 1 /*fifl_read.c*/
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <errno.h>
 5 #include <fcntl.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 #define FIFO "/tmp/myfifo"
10 int main(int argc,char** argv)
11 {
12     char buf_r[100];
13     int fd;
14     int nread;
15     /*创建有名管道,并设置相应的权限*/
16     if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
17         printf("cannot create fifoserver\n");
18     printf("Preparing for reading bytes...\n");
19     memset(buf_r,0,sizeof(buf_r));
20     /*打开有名管道,并设置非阻塞标志*/
21     fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
22     if(fd== -1)
23     {
24         perror("open ERROR");
25         exit(1);
26     }
27     while(1)
28     {
29         memset(buf_r,0,sizeof(buf_r));
30         if((nread=read(fd,buf_r,100))== -1)
31         {
32             if(errno==EAGAIN)
33             printf("no data yet\n");
34         }
35         printf("read %s from FIFO\n",buf_r);
36         sleep(1);
37     }
38     pause();
39     unlink(FIFO);
40 }
 1 /*fifo_write.c*/
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <errno.h>
 5 #include <fcntl.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 #define FIFO "/tmp/myfifo"
10 int main(int argc,char** argv)
11 {/*参数为即将写入的字节数*/
12     int fd;
13     char w_buf[100];
14     int nwrite;
15     if(fd== -1)
16     if(errno==ENXIO)
17         printf("open error; no reading process\n");
18     /*打开FIFO管道,并设置非阻塞标志*/
19     fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);
20     if(argc==1)
21         printf("Please send something\n");
22     strcpy(w_buf,argv[1]);
23     /*向管道中写入字符串*/
24     if((nwrite=write(fd,w_buf,100))== -1)
25     {
26         if(errno==EAGAIN)
27             printf("The FIFO has not been read yet.Please try later\n");
28     }
29     else
30         printf("write %s to the FIFO\n",w_buf);
31 }

开2个终端,切换到root执行,首先启动读管道程序。由于这是非阻塞管道,因此在建立管道之后程序就开始循环从管道里读出内容。在启动了写管道程序后,读进程能够从管道里读出用户的输入内容。

技术分享

技术分享

技术分享

 

2.信号通信

 技术分享

用户进程对信号的响应可以有3 种方式。
· 忽略信号,即对信号不做任何处理,但是有两个信号不能忽略,即SIGKILL 及SIGSTOP。
· 捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。
· 执行缺省操作,Linux 对每种信号都规定了默认操作。

技术分享

技术分享

 

发送信号的函数主要有kill()、raise()、alarm()以及pause(),raise函数允许进程向自身发送信号。alarm 也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,它就向进程发送SIGALARM 信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
pause 函数是用于将调用进程挂起直至捕捉到信号为止。这个函数很常用,通常可以用于判断信号是否已到。

技术分享

                  raise函数

技术分享

技术分享

技术分享

信号的处理:

技术分享

信号集函数组处理信号时涉及一系列的函数,这些函数按照调用的先后次序可分为以下几大功能模块:创建信号集合、登记信号处理器以及检测信号。
其中,创建信号集合主要用于创建用户感兴趣的信号,其函数包括以下几个。
· sigemptyset:初始化信号集合为空。
· sigfillset:初始化信号集合为所有信号的集合。
· sigaddset:将指定信号加入到信号集合中去。
· sigdelset:将指定信号从信号集中删去。
· sigismember:查询指定信号是否在信号集合之中。

技术分享

 

3.共享内存

由于多个进程共享一段内存,因此需要依靠某种同步机制,如互斥锁和信号量等。

共享内存的实现分为两个步骤,第一步是创建共享内存,用到的函数是shmget,第二步映射共享内存,也就是把这段创建的共享内存映射到具体的进程空间去,这里使用的函数是shmat。到这里就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O 读写命令对其进行操作。除此之外,还有撤销映射的操作,其函数为shmdt。

技术分享

                shmat函数

技术分享

技术分享

命令ipcs用于报告进程间通信机制状态的命令,它可以查看共享内存、消息队列等各种进程间通信机制的情况。

 1 /*shmadd.c*/
 2 #include <sys/types.h>
 3 #include <sys/ipc.h>
 4 #include <sys/shm.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #define BUFSZ 2048
 8 int main()
 9 {
10     int shmid;
11     char *shmadd;
12     /*创建共享内存*/
13     if((shmid=shmget(IPC_PRIVATE,BUFSZ,0666))<0)
14     {
15         perror("shmget ERROR");
16         exit(1);
17     }
18     else
19         printf("created shared-memory: %d\n",shmid);
20     system("ipcs -m");
21     /*映射共享内存*/
22     if((shmadd=shmat(shmid,0,0))<(char *)0)
23     {
24         perror("shmat");
25         exit(1);
26     }
27     else
28         printf("attached shared-memory\n");
29     /*显示系统内存情况*/
30     system("ipcs -m");
31     /*删除共享内存*/
32     if((shmdt(shmadd))<0)
33     {
34         perror("shmdt");
35         exit(1);
36     }
37     else
38         printf("deleted shared-memory\n");
39     system("ipcs -m");
40     exit(0);
41 }

 

4.消息队列

消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列这四种操作。其中创建或打开消息队列使用的函数是msgget,这里创建的消息队列的数量会受到系统消息队列数量的限制;添加消息使用的函数是msgsnd 函数,它把消息添加到已打开的消息队列末尾;读取消息使用的函数是msgrcv,它把消息从消息队列中取走,与FIFO不同的是,这里可以指定取走某一条消息;最后控制消息队列使用的函数是msgctl,它可以完成多项功能。

技术分享

技术分享

技术分享

技术分享

 1 /*msg.c*/
 2 #include <sys/types.h>
 3 #include <sys/ipc.h>
 4 #include <sys/msg.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <unistd.h>
 8 #include <string.h>
 9 #define BUFSZ 512
10 struct message
11 {
12     long msg_type;
13     char msg_text[BUFSZ];
14 };
15 int main()
16 {
17     int qid;
18     key_t key;
19     int len;
20     struct message msg;
21     /*根据不同的路径和关键表示产生标准的key*/
22     if((key=ftok(".",a))== -1)
23     {
24         perror("ftok");
25         exit(1);
26     }
27     /*创建消息队列*/
28     if((qid=msgget(key,IPC_CREAT|0666))== -1)
29     {
30         perror("msgget");
31         exit(1);
32     }
33     printf("opened queue %d\n",qid);
34     puts("Please enter the message to queue:");
35     if((fgets((&msg)->msg_text,BUFSZ,stdin))==NULL)
36     {
37         puts("no message");
38         exit(1);
39     }
40     msg.msg_type = getpid();
41     len = strlen(msg.msg_text);
42     /*添加消息到消息队列*/
43     if((msgsnd(qid,&msg,len,0))<0)
44     {
45         perror("message posted");
46         exit(1);
47     }
48     /*读取消息队列*/
49     if(msgrcv(qid,&msg,BUFSZ,0,0)<0)
50     {
51         perror("msgrcv");
52         exit(1);
53     }
54     printf("message is:%s\n",(&msg)->msg_text);
55     /*从系统内核中移走消息队列。*/
56     if((msgctl(qid,IPC_RMID,NULL))<0)
57     {
58         perror("msgctl");
59         exit(1);
60     }
61     exit(0);
62 }

 

进程间通信

标签:

原文地址:http://www.cnblogs.com/ht-beyond/p/4305714.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!