标签:
现在在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