操作系统实验指导书
if((childpid=fork())= =1) {
perror(\exit(1); }
if(childpid= =0) {
/*Child process closes up in put side of pipe*/ close(fd[0]);
/*Send\write(fd[1],string,strlen(string)); exit(0); } else {
/*Parent process closes up out put side of pipe*/ close(fd[1]);
/*Readinastringfromthepipe*/
nbytes=read(fd[0],readbuffer,sizeof(readbuffer)); printf(\} }
四、参考程序 #include
main( ) {
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd); /*创建一个管道*/ while ((pid1=fork( ))= =-1); if(pid1= =0) {
lockf(fd[1],1,0);
sprintf(outpipe,\
/*把串放入数组outpipe中*/
write(fd[1],outpipe,50); /*向管道写长为50字节的串*/ sleep(5); /*自我阻塞5秒*/ lockf(fd[1],0,0); exit(0);
}
else
{
while((pid2=fork( ))= =-1); if(pid2= =0)
31
操作系统实验指导书
{ lockf(fd[1],1,0); /*互斥*/
sprintf(outpipe,\
write(fd[1],outpipe,50); sleep(5);
lockf(fd[1],0,0); exit(0); }
else
{ wait(0); /*同步*/
read(fd[0],inpipe,50); /*从管道中读长为50字节的串*/
printf(\
wait(0);
read(fd[0],inpipe,50); printf(\
exit(0); } }
}
五、运行结果
延迟5秒后显示
child 1 process is sending message!
再延迟5秒
child 2 process is sending message!
六、思考题
1、程序中的sleep(5)起什么作用?
2、子进程1和2为什么也能对管道进行操作?
32
操作系统实验指导书
实验三 进程间的通信 (二)消息的发送与接收实验
实验目的
1、了解什么是消息 2、熟悉消息传送的机理
实验内容
消息的创建、发送和接收。使用系统调用msgget( ),msgsnd( ),msgrev( ),及msgctl( )编制一长度为1k的消息发送和接收的程序。
实验指导
一、什么是消息
消息(message)是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列。消息使用二种重要的数据结构:一是消息首部,其中记录了一些与消息有关的信息,如消息数据的字节数;二个消息队列头表,其每一表项是作为一个消息队列的消息头,记录了消息队列的有关信息。
1、消息机制的数据结构 (1)消息首部
记录一些与消息有关的信息,如消息的类型、大小、指向消息数据区的指针、消息队列的链接指针等。
(2)消息队列头表
其每一项作为一个消息队列的消息头,记录了消息队列的有关信息如指向消息队列中第一个消息和指向最后一个消息的指针、队列中消息的数目、队列中消息数据的总字节数、队列所允许消息数据的最大字节总数,还有最近一次执行发送操作的进程标识符和时间、最近一次执行接收操作的进程标识符和时间等。
2、消息队列的描述符
UNIX中,每一个消息队列都有一个称为关键字(key)的名字,是由用户指定的;消息队列有一消息队列描述符,其作用与用户文件描述符一样,也是为了方便用户和系统对消息队列的访问。
二、涉及的系统调用 1. msgget( )
获得一个消息的描述符,该描述符指定一个消息队列以便用于其他系统调用。如果希望创建一个新的消息队列,或者希望存取一个已经存在的消息队列,你可以使用系统调用msgget()。
该函数使用头文件如下: #include
(msgget()、msgsnd()、msgrcv ()、msgctl()所用头文件相同) 原型:int msgget (key-t key, int msgflag)
返回值:如果成功,返回消息队列标识符,如果失败,则返回-1。
33
操作系统实验指导书
其中key是类似一个长整型,可由用户设定也可通过ftok()获得。msgflg的值是八进制的消息队列操作权和控制命令的组合。附: 操作允许权 八进制整数 用户可读 用户可写 同组可读 同组可写 其它可读 0400 0200 0040 0020 0004 0002 其它可写 操作权可相加而派生,如用户“可读”、“可写” 的 权 限 为0400|0200=0600。 控制命令可取IPC_CREAT或IPC_EXCL。IPC_CREAT如果内核中没有此队列,则创建它。IPC_EXCL当和IPC_CREAT一起使用时,如果队列已经存在,则失败。
如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。
如果要创建一个key=888且属主和同组可读写的消息队列,执行以下系统调用msgget(888,0660|IPC_CREAT)。下面看一个打开和创建一个消息队列的例子: int open_queue(key_t keyval){ int qid;
if((qid=msgget(keyval,IPC_CREAT|0660))= =-1){ return(-1); }
return(qid); }
2. msgsnd()
发送一消息。向指定的消息队列发送一个消息,并将该消息链接到该消息队列的尾部。 系统调用格式:
msgsnd(msgqid,msgp,size,flag) 该函数使用头文件如下: #include
int msgsnd(msgqid,msgp,size,flag) I int msgqid,size,flag; struct msgbuf * msgp;
其中msgqid是返回消息队列的描述符;msgp是指向用户消息缓冲区的一个结构体指针。缓冲区中包括消息类型和消息正文,即
{
long mtype; /*消息类型*/ char mtext[ ]; /*消息的文本*/ }
size指示由msgp指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX( )系统可调用参数来确定。flag规定当核心用尽内部缓冲空间时应执行的动作:进程是等待,还是立即返回。若在标志flag中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过最大值时,或系统范围的消息数超过某一最大值时,调用msgsnd进程睡眠。若是设置IPC_NOWAIT,则在此情况下,msgsnd立即返回。
对于msgsnd( ),核心须完成以下工作:
34
操作系统实验指导书
(1)对消息队列的描述符和许可权及消息长度等进行检查。若合法才继续执行,否则返回;
(2)核心为消息分配消息数据区。将用户消息缓冲区中的消息正文,拷贝到消息数据区;
(3)分配消息首部,并将它链入消息队列的末尾。在消息首部中须填写消息类型、消息大小和指向消息数据区的指针等数据;
(4)修改消息队列头中的数据,如队列中的消息数、字节总数等。最后,唤醒等待消息的进程。
3. msgrcv( )
接受一消息。从指定的消息队列中接收指定类型的消息。 系统调用格式:
msgrcv(msgqid,msgp,size,type,flag) 本函数使用的头文件如下: #include
#include
int msgrcv(msgqid,msgp,size,type,flag) int msgqid,size,flag;
struct msgbuf *msgp; long type; 其中,msgqid,msgp,size,flag与msgsnd中的对应参数相似,type是规定要读的消息类型,flag规定倘若该队列无消息,核心应做的操作。如此时设置了IPC_NOWAIT标志,则立即返回,若在flag中设置了MS_NOERROR,且所接收的消息大于size,则核心截断所接收的消息。
对于msgrcv系统调用,核心须完成下述工作:
(1)对消息队列的描述符和许可权等进行检查。若合法,就往下执行;否则返回; (2)根据type的不同分成三种情况处理:
type=0,接收该队列的第一个消息,并将它返回给调用者; type为正整数,接收类型type的第一个消息;
type为负整数,接收小于等于type绝对值的最低类型的第一个消息。
(3)当所返回消息大小等于或小于用户的请求时,核心便将消息正文拷贝到用户区,并从消息队列中删除此消息,然后唤醒睡眠的发送进程。但如果消息长度比用户要求的大时,则做出错返回。
4. msgctl( )
消息队列的操纵。读取消息队列的状态信息并进行修改,如查询消息队列描述符、修改它的许可权及删除该队列等。
系统调用格式:
msgctl(msgqid,cmd,buf); 本函数使用的头文件如下: #include
int msgctl(msgqid,cmd,buf); int msgqid,cmd;
struct msgqid_ds *buf;
其中,函数调用成功时返回0,不成功则返回-1。buf是用户缓冲区地址,供用户存放控制参数和查询结果;cmd是规定的命令。命令可分三类:
(1)IPC_STAT。查询有关消息队列情况的命令。如查询队列中的消息数目、队列中的最大字节数、最后一个发送消息的进程标识符、发送时间等;
35