ImageVerifierCode 换一换
格式:DOCX , 页数:23 ,大小:101.31KB ,
资源ID:9122541      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-9122541.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(实验三进程间通信 1.docx)为本站会员(b****8)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

实验三进程间通信 1.docx

1、实验三进程间通信 1实验三 线程控制和进程间通信一、实验目的通过Linux管道通信机制、消息队列通信机制的使用,加深对不同类型的进程通信方式的理解。二、实验内容: 1. 熟悉Linux的管道通信机制2. 熟悉Linux的消息队列通信机制三、思考1. 有名管道和无名管道之间有什么不同?2. 管道的读写与文件的读写有什么异同?3. Linux消息队列通信机制中与教材中的消息缓冲队列通信机制存在哪些异同?四、实验指导Linux管道通信机制管道是所有UNIX都提供的一种进程间通信机制,它是进程之间的一个单向数据流,一个进程可向管道写入数据,另一个进程则可以从管道中读取数据,从而达到进程通信的目的。1.

2、无名管道 无名管道通过pipe()系统调用创建,它具有如下特点:(1) 它只能用于具有亲缘关系的进程(如父子进程或者兄弟进程)之间的通信。(2) 管道是半双工的,具有固定的读端和写端。虽然pipe()系统调用返回了两个文件描述符,但每个进程在使用一个文件描述符之前仍需先将另一个文件描述符关闭。如果需要双向的数据流,则必须通过两次pipe()建立起两个管道。(3) 管道可以看成是一种特殊的文件,对管道的读写与文件的读写一样使用普通的read、write等函数,但它不是普通的文件,也不属于任何文件系统,而只存在于内存中。2.pipe系统调用 (1)函数原型 #include int pipe(in

3、t filedes2);(2)参数filedes参数是一个输出参数,它返回两个文件描述符,其中filedes0指向管道的读端,filedes1指向管道的写端。(3)功能pipe在内存缓冲区中创建一个管道,并将读写该管道的一对文件描述符保存在filedes所指的数组中,其中filedes0用于读管道,filedes1用于写管道。(4)返回值成功返回0;失败返回-1,并在error中存入错误码。(5)错误代码EMFILE:进程使用的文件描述符过多 ENFILE :系统文件表已满 EFAULT :非法参数filedes 3.无名管道的阻塞型读写管道缓冲区有4096B的长度限制,因此,采用阻塞型读写方

4、式时,当管道已经写满时,写进程必须等待,直到读进程取走信息为止。同样,读空的管道时,也可能会引起进程阻塞。当管道大小(管道缓冲区中待读的字节数)为p,而用户进程请求读n个字节时:(1) 若不存在写进程: p=0,则返回0; 0pn,则读得p个字节,返回p,管道缓冲区中还剩0个字节; pn,则读得n个字节,返回n,管道缓冲区中还剩pn个字节;(2) 若存在写进程,且写进程没因写管道而阻塞时: p=0,读进程阻塞等待数据被写入管道; 0pn,则读得p个字节,返回p,管道缓冲区中还剩0个字节; pn,则读得n个字节,返回n,管道缓冲区中还剩pn个字节;(3) 若存在写进程,且写进程因写管道而阻塞时:

5、0pn,则读管道,当管道缓冲区变空时,阻塞,等待数据被写入。最后,返回实际读得的字节数;pn,则读得n个字节,返回n,管道缓冲区中还剩pn个字节。 当管道缓冲区中有u个字节未用,而用户进程请求写入n个字节时:(1) 若不存在读进程,则向写管道的进程将发SIGPIPE信号,并返回EPIPE。(2) 若存在至少一个读进程: un4096 则写进程等待,直到有n-u个字节被释放为止,写入n个字节,返回n; n4096 则写入n个字节(必要时等待)并返回n; un 写入n个字节,返回n。4.无名管道使用实例/pipeDemo.c#include #include #include #include #

6、define BUFNUM 60int main(void) int n; int fd2; pid_t pid; char bufBUFNUM; if (pipe(fd) 0) fprintf(stderr,Creat Pipe Error:%s n,strerror(errno); exit(EXIT_FAILURE); if (pid = fork() 0) /* parent */ close(fd0); write(fd1, Im your father.n, 17); write(fd1, Im waiting for your termination.n, 35); wait(N

7、ULL); else /* child */ close(fd1); n = read(fd0, buf, BUFNUM); printf(%d bytes read:%s,n,buf); return 0;上述程序父进程创建一个管道,然后再创建一个子进程,由于子进程是父进程的精确拷贝,因此子进程也复制了父进程的文件描述符表信息,从而可以访问到父进程创建的管道。接着父进程关闭管道的读端,并向管道写入一些信息;而子进程则关闭管道的写端,并从读端获得父进程写入的信息。请思考:(1)如果将pipe()调用放在fork()之后进行,那么上述父子进程是否还能进行管道通信?(2)上述程序运行后,子进程从管

8、道中读到的信息是17个字节,还是52个字节,还是60个字节?为什么?(3)如果子进程执行read()时,第3个参数的值为5,那么程序运行的结果会怎样?管道中未取走的信息在读端未关闭以前会不会消失?(4)父子进程的执行顺序是否会影响到程序运行的结果?5.有名管道 管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信

9、(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。有名管道通过mkfifo创建:#include #include int mkfifo(const char * pathname, mode_t mode)该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。 有名管道创建成功

10、,mkfifo()返回0;否则返回-1。如果mkfifo的第一个参数是一个已经存在的路径名时,错误代码中会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。与普通文件类似,有名管道在使用之前必须先进行open操作,具体类似于文件的打开方式:#include #include #include int open(const char *pathname, int flags);对有名管道的open操作必须遵循下列规则:(1)如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将

11、成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。(2)如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。一旦打开操作成功,便可通过返回的文件描述符,利用read、write系统调用对管道进行读写操作,读写完成应使用close系统调用关闭有名管道。(3)有名管道使用实例/* namedPipeDemo.c */#in

12、clude #include #include #include #include #include #define FIFO_NAME /tmp/myfifomain() int fd; char w_buf50; int w_num; / 若fifo已存在,则直接使用,否则创建它 if(mkfifo(FIFO_NAME,0777)0)&(errno!=EEXIST) printf(cannot create fifo.n); exit(1); /以阻塞型只写方式打开fifo fd=open(FIFO_NAME,O_WRONLY,0); if(fd=-1) if(errno=ENXIO) p

13、rintf(cannot open fifo for read.n); exit(1); / 通过键盘输入字符串,再将其写入fifo,直到输入exit为止 while(1) printf(please input something:); scanf(%s,w_buf); w_num=write(fd,w_buf,strlen(w_buf); printf(real write num is %dn,w_num); if(strcmp(w_buf,exit)=0) break; /* namedPipeDemo2.c */#include #include #include #include

14、#define FIFO_NAME /tmp/myfifomain() char r_buf50; int fd; int r_num; / 若fifo已存在,则直接使用,否则创建它 if(mkfifo(FIFO_NAME,0777)0)&(errno!=EEXIST) printf(cannot create fifo.n); exit(1); /以阻塞型只读方式打开fifo fd=open(FIFO_NAME,O_RDONLY,0); if(fd=-1) printf(open %s for read errorn); exit(1); / 通过键盘输入字符串,再将其写入fifo,直到输入

15、exit为止 while(1) memset(r_buf,0,sizeof(r_buf); r_num=read(fd,r_buf,50); if(r_num=-1) if(errno=EAGAIN) printf(no data avlaiblen); printf( %d bytes read:%sn,r_num,r_buf); if(strcmp(r_buf,exit)=0) break; sleep(1); unlink(FIFO_NAME);/删除fifo程序编译和运行结果: Linux消息队列通信机制Linux系统中,若干个进程可以共享一个消息队列,系统允许其中的一个或多个进程向消

16、息队列写入消息,同时也允许一个或多个进程从消息队列中读取消息,从而完成进程之间的信息交换,这种通信机制被称作消息队列通信机制。1.数据结构(1)消息缓冲区struct msgbuf消息缓冲区是用来存放消息内容的结构体,而且这个结构体的第一个成员必须是一个大于0的长整数,表示对应消息的类型;不过,系统对结构体中其余成员的类型不做任何限制。include/linux/msg.h中给出的消息缓冲格式如下:struct msgbuf /* 消息定义的参照格式 */ long mtype; /* 消息类型(大于0的长整数) */ char mtext1; /*消息正文*/;应用程序员可以重新定义消息缓冲

17、区结构体,其中,成员(mtext)不仅能定义为长度为1的字符数组,也可以定义成长度大于1的字符数组,或定义成其他的数据类型,Linux也允许消息正文的长度为0,即结构体中没有mtext域。虽然,Linux没限定mtext的类型,但却限定了消息的长度,一个消息的最大长度由宏MSGMAX决定,根据版本的不同,其取值可能为8192或其他值。(2)消息结构struct msg消息队列中的每个消息节点中不仅包含了消息内容,还包含了一些其他信息,消息节点由消息结构来描述。include/linux/msg.h中给出的消息结构格式如下:struct msg struct msg *msg_next; /*消

18、息队列链接指针,指向队列中的下一条消息 */ long msg_type; /*消息类型,同struct msgbuf中的mtype*/ char *msg_spot; /* 消息正文的地址,指向msgbuf的消息正文 */ time_t msg_stime; /* 消息发送的时间 */ short msg_ts; /* 消息正文的大小 */;(3)IPC对象访问权限struct ipc_permstruct ipc_permkey_t key; /* IPC对象键值 */ushort uid; /* owner euid and egid */ushort gid;ushort cuid;

19、/* creator euid and egid */ushort cgid;ushort mode; /* 访问权限 */ushort seq; /* slot usage sequence number,即IPC对象使用频率信息 */;其中:key 是IPC对象(例如消息队列,共享存储器等)的键值,每个IPC对象都关联着一个唯一的长整型的键值,不同的进程通过相同的键值可访问到同一个IPC对象。用户进程在创建IPC对象时可以指定key为某个大于0的整数,此时,需要用户自己保证该key值不与系统中存在的其他IPC键值相冲突。更常用的方式是通过函数调用ftok(pathname,proj_jd)

20、请求系统为用户进程生成一个键值,其中的pathname是一个实际存在的文件的路径名,而且用户进程具有对该文件的访问权限,proj_jd是一个整数,但ftok只会用到其低8位的值(该值不能为0),只要路径名访问到的是同一个文件,而且proj_jd的低8位的值相同,则ftok()调用便将产生相同的键值;如果使用不同的文件路径名和proj_jd,虽然系统不能保证、但通常生成的键值是不同的。mode 中给出了该IPC对象的访问权限,它可以是下列权限的组合: 访问权限 八进制整数 拥有者可读 0400 拥有者可写 0200 同组用户可读 0040 同组用户可写 0020 其他用户可读 0004 用户用户

21、可写 0002(3)消息队列结构体struct msqid_ds系统中每个消息队列由一个struct msqid_ds类型的变量来描述,struct msqid_ds的格式如下:struct msqid_ds struct ipc_perm msg_perm; /* 消息队列访问权限*/ struct msg *msg_first; /* 队列上第一条消息,即链表头*/ struct msg *msg_last; /* 队列中的最后一条消息,即链表尾 */ time_t msg_stime; /* 发送给队列的最后一条消息的时间 */ time_t msg_rtime; /* 从消息队列接收到

22、最后一条消息的时间 */ time_t msg_ctime; /* 最后修改队列的时间*/ ushort msg_cbytes; /*队列上所有消息总的字节数 */ ushort msg_qnum; /*当前队列上消息的个数 */ ushort msg_qbytes; /* 队列允许的最大的字节数 */ ushort msg_lspid; /* 发送最后一条消息的进程的pid */ ushort msg_lrpid; /* 接收最后一条消息的进程的pid */;Linux还通过宏MSGMNB限定了一个消息队列的最大长度(队列中所有消息总的字节数)。2.消息队列相关的系统调用Linux提供了一组

23、消息队列相关的系统调用来方便用户进行消息通信。(1)msgget系统调用函数原型:#include #include #include int msgget(key_t key, int msgflg);参数:key key为0(IPC_PRIVATE),则创建一个新的消息队列;否则,key为一个大于0的长整数,它对应于消息队列的键值,通常是通过ftok()函数生成的。msgflg 对消息队列的访问权限和控制命令的组合。其中访问权限见“IPC对象访问权限struct ipc_perm”部分的说明。而控制命令IPC_CREAT表示,如果key对应的消息队列不存在,则创建它;而IPC_EXCL必须

24、与IPC_CREATT一起使用,它表示:如果key对应的消息队列不存在,则创建一个新的队列,否则返回-1。功能: 如果IPC_CREAT单独使用,semget()为一个新创建的消息队列返回标识数,或者返回具有相同键值的已存在消息队列标识数。如果IPC_EXCL与IPC_CREAT一起使用,要么创建一个新的队列并返回它的标识数,如果队列已存在,则返回-1。返回值: 成功,返回消息队列的标识数;出错,返回-1,同时将错误代码存放在error中。对于新创建的消息队列,其msqid_ds结构成员变量的初值设置如下: msg_qnum、msg_lspid、msg_lrpid设置为0;msg_stime、

25、msg_rtime设置为0;msg_ctime设置为当前时间;msg_qbytes设成系统的限制值,即宏MSGMNB;msgflg的读写权限写入msg_perm.mode中;msg_perm结构的uid和cuid成员被设置成当前进程的有效用ID,id和cuid成员被设置成当前进程的有效组ID。错误代码:EACCES:指定的消息队列已存在,但调用进程没有权限访问它EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志ENOENT:key指定的消息队列不存在同时msgflg中没有指定IPC_CREAT标志ENOMEM:需要建立消息队列,但内存不足

26、ENOSPC:需要建立消息队列,但已达到系统的限制(2)msgsnd系统调用函数原型:#include #include #include int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);参数:msqid 消息队列的标识数msgp 存放欲发送消息内容的消息缓冲区指针msgsz 消息正文(而非整个消息结构)的长度msgflg 0 消息队列满时,msgsnd将会阻塞 IPC_NOWAIT 消息队列满时,msgsnd立即返回-1 MSG_NOERROR消息正文长度超过msgsz字节时,不报错,而是直接截去其中多

27、余的部分,并只将前面的msgsz字节发送出去功能: 在标识数为msqid的消息队列中添加一个消息,即向标识数为msqid的消息队列发送一个消息。返回值: 消息发送成功,返回0;否则返回-1,同时error中存有错误代码错误代码:EAGAIN 参数msgflg设为IPC_NOWAIT,而消息队列已满EACCESS无权限写入消息队列EFAULT 参数msgp指向的地址无法访问EIDRM 标识符为msqid的消息队列已被删除EINTR 队列已满而处于阻塞的情况下,被信号唤醒EINVAL 无效的参数msqid、或消息类型type小于等于0、或msgsz为负数或超过系统限制值MSGMAXENOMEM 系统无足够内存空间存放msgbuf消息的副本(3)msgrcv系统调用函数原型:#include #include #include ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);参数:msqid 消息队列的标识数msgp 存放欲接收消息内容的消息缓冲区指针msgsz 消息正文(而非

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2