4.2 管道通信
管道有两种:命名管道和无名管道。pipe为无名管道,两个父子进程之间可
以利用pipe机构传送大量信息。其中一个进程利用write向管道写数据,另一个进程利用read读管道中的数据。 利用pipe建立管道: int pipe_fd[2]; pipe(pipe_fd);
其中在pipe_fd[0]中返回pipe读通道打开的文件号,在pipe_fd[1]中返回pipe写通道打开的文件号。一般情况下,进程定义pipe后,会创建一个或几个子进程,父子进程利用pipe进行通信,于是pipe被父子进程共享,其中一个进程只利用它的发送端,另一个只用它的接收端。利用发送端的进程就应当关闭其接收端,利用接收端的进程应当关闭其发送端。管道通信要加头文件unistd.h。 例1:父子进程利用pipe通信。父进程利用一个pipe发送消息,子进程利用该pipe接收父进程发来的信息;子进程利用另一个pipe向父进程发送应答,父进程利用该pipe接收应答。
#include
char father[]={“the message from father.\\n”}; char child[]={“the message from child.\\n”}; main() {
int chan1[2],chan2[2]; char buff[50]; pipe(chan1); pipe(chan2); if(fork()) {
close(chan1[0]);
close(chan2[1]);
write(chan1[1],father,strlen(farther)); close(chan1[1]);
read(chan2[0],buff,50);
printf(“father process:%s\\n”,buff); }else{ close(chan1[1]); close(chan2[0]); read(chan1[0],buff,50);
24
printf(“child process:%s\\n”,buff); write(chan2[1],child,strlen(child)); close(chan2[1]);
} }
运行结果:
child process:the message from father. father process:the message from child.
例2:编制一段程序,实现进程的管道通信。使用系统调用pipe()建立一条管道先。两个子进程p1和分别向管道各写一句话: Child 1 process is sending a message! Child 2 process is sending a message!
而父进程则从管道中读出来自于两个子进程的信息并显示在屏幕上。
#include
int fd[2];
char outpipe[100],inpipe[100]; pipe(fd);
while ((pid1=fork())= =-1); if (pid1= =0) {
printf”p1\\n”); lockf(fd[1],1,0);
sprintf(outpipe,”child 1 process is sending a message!”); write(fd[1],outpipe,50); sleep(1); //自我阻塞1秒 lockf(fd[1],0,0); exit(0);
} else {
while ((pid2=fork())= =-1); if (pid2= =0) {
printf(“p2\\n”); lockf(fd[1],1,0);
sprintf(outpipe,”child 2 process is sending a message!”); write(fd[1],outpipe,50);
25
sleep(1);
lockf(fd[1],0,0); exit(0); } else {
printf(“parent\\n”); wait(0); //同步
read(fd[0],inpipe,50); //从管道中读 printf(“%s\\n”,inpipe); wait(0);
read(fd[0],inpipe,50); printf(“%s\\n”,inpipe); exit(0); } } }
运行结果:
child 1 process is sending a message! child 2 process is sending a message!
4.3消息通信 1、msgget(key,flag):
获得一个消息的描述符,该描述符指定一个消息队列以便用于其他系统调
用。
该函数使用偷文件如下: #include
int msgget(key,flag) key_tkey; int flag;
语法格式:msgqid=msgget(key,flag)
其中:msgid是该系统调用返回的描述符,失败则返回-1;flag 本身由操作
允许权和控制命令值相“或”得到。
如:IP_CREAT|0400 是否该队列应被创建;
26
IP_EXCL |0400 是否该队列的创建应是互斥的;等。
2、msgsnd(id,msgp,size,flag):
发送一消息。
该函数是用头文件如下: #include
int msgnd(id,msgp,size,flag) int id,size,flag; struct msgbuf * msgp;
其中:id是返回消息队列的描述符;msgp是指向用户存储区的一个构造体
指针,size指示由msgp指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX系统可调用参数来确定。flag规定当核心用尽内部缓冲空间时应执行的动作;若在标志flag中末设置IPC_NOWAIT位,则当该消息队列中字节数超过一最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd进程睡眠。若是设置IPC_NOWAIT,则在此情况下,msgsnd立即返回。
3、msgrcv(id,msgp,size,type,flag):
接受一消息。
该函数调用使用头文件如下: #include
int msgrcv(id,msgp,size,type,flag) int id,size,type,flag; struct msgbuf * msgq;
struct sgbuf{long mtpe;chat mtext[];};
27
语法格式:
count=msgrcv(id,msgp,size,type,flag)
其中:id是用来存放欲接收消息的拥护数据结构的地址;size是msgp中数
据数组的大小; type是用户要读的消息类型:
type为0:接收该队列的第一个消息; type为正:接收类型type的第一个消息;
type为负:接收小于或等于type绝对值的最低类型的第一个消息。 flag规定倘若该队列无消息,核心应当做什么事,如果此时设置了
IPC_NOWAIT标志,则立即返回,若在flag中设置了MSG_NOERROR,且所接收的消息大小大于size,核心截断所接受的消息。
count是返回消息正文的字节数。
4、msgctl(id,cmd,buf):
查询一个消息描述符的状态,设置它的状态及删除一个消息描述符。 调用该函数使用头文件如下: #include
int msgctl(id,cmd,buf) int id,cmd;
struct msgbuf * msgq; struct msqid_ds * buf;
其中:函数调用成功时返回0,调用不成功时返回-1。id用来识别该消息的
描述符;cmd规定命令的类型。
IPC_START将与id相关联的消息队列首标读入buf。
IPC_SET为这个消息序列设置有效的用户和小组标识及操作允许权和字节
的数量。
IPC_RMID删除id的消息队列。
buf是含有控制参数或查询结果的用户数据结构的地址。
28