《第7章Linux环境编程5学时》由会员分享,可在线阅读,更多相关《第7章Linux环境编程5学时(77页珍藏版)》请在金锄头文库上搜索。
1、第7章 Linux环境编程 本章作业 两个C语言源程序文件image.c和search.c共同完成一项任务,其中:image.c调用了search.h(保存在/temp目录中)中的函数dissort(),search.c使用了search.h中的变量。 在Linux环境下用gcc命令进行编译,要求如下: 1.生成可执行程序文件名为imgsch; 2.加入供调试程序gdb使用的附加信息; 请写出: 1、gcc编译命令 2、makefile文件编译命令:编译命令:$gcc-I/tmp-gcsearch.c$gcc-I/tmpgcimage.c$gccsearch.oimage.ooimgsch#对
2、对search.c文件单独编译,不进行连接,且告诉编译器头文件的位置文件单独编译,不进行连接,且告诉编译器头文件的位置#对对image.c文件单独编译,不进行连接文件单独编译,不进行连接#连接上述两行命令得到的连接上述两行命令得到的.o文件,生成可执行文件文件,生成可执行文件imgsch该选项,将告诉编译器在目标文件(即.o文件)中加入供调试器gdb使用的附加信息makefile文件的内容:文件的内容:imgsch:search.oimage.ogccsearch.oimage.ooimgschsearch.o:search.csearch.hgcc-I/tmpcsearch.cimage.o
3、:image.csearch.hgcc-I/tmpcimage.cclean:rmf*.o目标文件目标文件相依文件相依文件操作命令操作命令 主要内容v系统调用和库函数简介v文件操作v进程管理和同步v进程通信v内存管理7.1 系统调用和库函数v操作系统可以对外提供服务,并且通过不同的方式实现服务;v其中基本的两种服务方式就是: 系统调用 和 库函数 也是操作系统提供给用户的两种接口7.1.1 系统调用v系统调用像普通C语言函数调用那样,出现在C语言程序中;v一般的函数调用不能把进程状态从用户态变为内核态,而系统调用却可以,并将进程上下文从用户堆栈切换到系统堆栈。vLinux的系统调用是通过中断指
4、令实现的,v目前Linux系统中共定义了221个系统调用,所有的系统调用都在系统调用入口表中统一管理。基本概念:基本概念:基本概念:基本概念:操作系统和系统调用的关系vv操作系统负责直接与硬件交互,向用户程序提供公共服务,操作系统负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离;并使它们同硬件特性隔离;vv系统调用:系统调用: 是是LinuxLinux操作系统向用户程序提供支持的接口,操作系统向用户程序提供支持的接口, 通过这些接口,应用程序向操作系统请求服务,控制转向操通过这些接口,应用程序向操作系统请求服务,控制转向操作系统;作系统; 而操作系统在完成服务后,将控制和结果
5、返回给用户程序。而操作系统在完成服务后,将控制和结果返回给用户程序。 vv在在LinuxLinux系统中进程的两种运行模式:系统中进程的两种运行模式: 高优先级的内核模式、低优先级的用户模式。v内核运行在高优先级;v其它外围软件包括shell,编辑程序等都是在低优先级运行。 在用户程序执行过程中,如果使用了系统调用,或者发生了中断,就要运行操作系统程序,进程模式变为内核模式。系统进程只运行在内核模式u之所以采取不同的执行模式主要原因是为了保护,由于用户进程在较低的特权级上运行,它们将不能意外或故意破坏其它进程或内核。u用户进程如果要完成内核模式下才能执行的某些功能时,必须严格按照系统调用提供接
6、口,才能进入内核模式,完成相应的功能。 #include#include#includeintopen(constchar*path,intoflags);例:系统调用例:系统调用open可以打开一个指定的文件可以打开一个指定的文件使用open时所需包含的头文件系统调用open的使用格式u系统调用可以看作是所有Linux进程共享的、在特权方式下运行子程序库, u系统调用的主要功能是:使用户可以使用操作系统提供的有关设备管理、文件系统、进程控制、进程通讯、存储管理方面的功能,而不必要了解操作系统的内部结构、有关硬件的细节问题,从而减轻用户负担和保护系统以及提高资源利用率。 7.1.2 库函数v库
7、函数解决一些共性问题,为程序开发和执行提供方便的环境。如:C语言中常用的fopen()函数就是标准I/O库中的库函数。 基本概念:库函数基本概念:库函数基本概念:库函数基本概念:库函数简单地说,是把一些常用到的函数编写完,放到一个文件里供别人用;使用时把它所在的文件名用#include加到里面就可以了,一般是放到lib目录里。库函数运行在用户空间Linux系统分为三个层次:用户、核心以及硬件 系统调用是用户程序与核心间的边界,通过系统调用进程可由用户模式转入内核模式,在内核模式下完成一定的服务请求后在返回用户模式。 v库函数可以分为下面六大类: 文件管理: 对文件、目录进行创建、删除等; 状态
8、信息: 对日期、时间、内存容量等信息的格式化和存储 文件修改: 程序设计语言的支持: 程序装入和执行: 通信: 在进程、用户和不同计算机系统之间,进行信息传送7.1.3 调用方式uLinux系统中,系统调用和库函数都是以C函数的形式,提供给用户的;u在C语言程序中,对系统调用的使用方式和库函数的调用方式相同。即: 调用时提供实参的个数和类型等信息;7.2 文 件 操 作v常用的有关文件操作的系统调用有:creat,open,close,read,write,mkdir,rmdir,chdirLinux系统中“文件”的概念涉及内容较广,如:普通文件、设备文件、目录文件7.2.1 有关文件操作的系
9、统调用v例:例:#include#include#includeintcreat(constchar*pathname,mode_tmode);#include#include#includeintopen(constchar*path,intoflags);intopen(constchar*path,intoflags,mode_tmode);创建新文件创建新文件7.2.2 应用示例 下列代码说明,如何对普通文件、设备文件,通过系统调用进行读写和打开操作。其中用到的三个文件: fileexp.c:普通文件 /dev/null: 设备文件 /tmp/m1.c 普通文件对文件的打开标志: O_
10、CREAT 如果被打开的文件不存在,则创建它 O_RDONLY 只读方式打开 O_WRONLY 只写方式打开1.#include2.#include3.#include4.#include5.#include6.#include7.intmain(void)8.9.intfd1,fd2,fd3,nbytes;10.charbuf10;11.if(fd1=open(“fileexp.c,O_RDONLY)0)12.perror(openfileexp.c);13.exit(EXIT_FAILURE);14.15.if(fd2=open(/dev/null,O_WRONLY)0)16.perror
11、(open/dev/null);17.exit(EXIT_FAILURE);18.系统调用open所需要包含的头文件显示错误信息20.if(fd3=open(/tmp/m1.c,O_CREAT|O_WRONLY)0)27.if(write(fd2,buf,10)0)28.perror(write/dev/null);29.if(write(fd3,buf,nbytes)0)30.perror(“write/tmp/m1.c);31.write(STDOUT_FILENO,buf,10);32.33.close(fd1);34.close(fd2);35.close(fd3);36.exit(E
12、XIT_SUCCESS);37.使用系统调用read将文件fileexp.c中的内容读出10个字节,存放到char型数组buf中。返回值是实际读取的字节数使用系统调用write,将buf中的信息写入到标准输出,即:显示屏上。回顾第回顾第5章的内容:章的内容:每个进程在创建时自动打开每个进程在创建时自动打开3个文件:个文件:标准输入、标准输出、标注错误输出,标准输入、标准输出、标注错误输出,分别对应文件描述符分别对应文件描述符0,1,2.在在中定义三个宏,中定义三个宏,STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO分别表示这些数字分别表示这些数字.进程控制块使用
13、文件时的逻辑结构进程控制块使用文件时的逻辑结构根目录节点当前目录节点保存该进程打开文件的有关保存该进程打开文件的有关信息。最多打开信息。最多打开256个文件个文件文件在文件在VFS中的索引节点中的索引节点第四章中第四章中I/O重定向中讲到的重定向中讲到的文件描述符(如:文件描述符(如:0,1,2)就是)就是fd数组的下标数组的下标索引节点表7.3 进 程 控 制u进程控制中主要涉及到进程的创建、睡眠和退出等;u进程是一个指令执行流及其执行环境, 其执行环境是一个系统资源的集合,这些资源在Linux中 被抽象成各种数据对象:进程控制块、虚存空间、文件系统,信号处理函数u所以创建一个进程的过程就是
14、这些数据对象的创建过程;7.3.1 有关进程控制的系统调用v在Linux中主要提供了fork、exec、clone的进程创建方法,sleep的进程睡眠和exit的进程退出调用;v另外Linux还提供了父进程等待子进程结束的系统调用wait。 v操作系统可以对外提供服务,是通过两种不同的方式来实现:系统调用系统调用 和 库函数库函数。知识点回顾:系统调用和库函数知识点回顾:系统调用和库函数应用程序操作系统用户接口系统调用和库函数请求服务返回结果硬件直接交互v例如:例如:#include#includepid_tfork(void);#include#includepid_tgetpid(void
15、);pid_tgetppid(void);创建一个子进程,有创建一个子进程,有两个返回值!两个返回值!返回当前进程的返回当前进程的ID号号返回父进程的返回父进程的ID号号执行原理: 操作系统执行fork函数时,会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这是因为: 这两个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝;fork fork 的执行原理的执行原理fork的英文意思是?叉子,分叉v可见,fork函数创建新进程后,父子进程按照一定的逻辑完成不同的工作,并拥有彼此独立的数据空间,就像是“分岔”了。这也是它
16、为什么叫fork的原因。 v如果父子进程之间需要进行协同操作,可以通过进程通信的不同方法实现。代码空间数据空间数据空间复制父进程子进程fork函数执行效果图父子进程按照一定的逻父子进程按照一定的逻辑选择该空间中的代码辑选择该空间中的代码执行。执行。v执行成功的情况下,两个返回值: 若执行成功,则在子子进程中返回值为返回值为0 0,父进程中返回值是子进程的进程ID;v若创建不成功,父进程会返回错误。 注意:fork 函数的独特之处编程时需要特别注意!使用方法使用方法使用方法使用方法#include#includepid_tfork(void);pid=fork();if(pid0)/父进程处理程
17、序父进程处理程序elseif(pid=0)/子进程处理程序子进程处理程序else/子进程创建出错子进程创建出错父子进程公用部分父子进程公用部分程序中的常用控制结构使用前的声明在父进程中变量pid的值是子进程的进程号;在子进程中变量pid的值是0fork调用是调用是linux系统中较难理解的概念之一,执行一次却返回两个值。系统中较难理解的概念之一,执行一次却返回两个值。下列程序对此进行实验论证:下列程序对此进行实验论证:下列程序对此进行实验论证:下列程序对此进行实验论证:intmain()inti;intpid;pid=fork();if(pid0)printf(“error”)elseif(e
18、lseif(pidpid=0)=0)for(i=1;i3;i+)for(i=1;i3;i+)printf(Thisprintf(Thisischildprocessn);ischildprocessn);elseelsefor(i=1;i3;i+)for(i=1;i3;i+)printf(Thisprintf(Thisisparentprocessn);isparentprocessn);运行过程演示与分析运行过程演示与分析运行过程演示与分析运行过程演示与分析 上述程序中的几个关键步骤: 1、用cd命令进入脚本exp1所在文件夹 2、用cat命令查看脚本内容 3、用gcc命令编译脚本,得到可执
19、行文件a.out 4、运行编译生成的可执行文件第2章第6章【课堂提问课堂提问课堂提问课堂提问】为什么为什么ifif和和elseelse部分都执行了?部分都执行了?【解答解答解答解答】forkfork函数执行成功时在函数执行成功时在 if 和和else部分均有符合要求的返回值;部分均有符合要求的返回值;因此,父子进程都被执行了;因此,父子进程都被执行了;而两种输出正式是来自于父子进程的不同执行结果。而两种输出正式是来自于父子进程的不同执行结果。课后作业: 请动手实现:在Linux环境中编写、编译、运行上述程序;观察与思考:当循环次数从3次扩大到100次,将出现什么结果?说明什么问题?应用示例应用
20、示例1intcom=0;main()pid_tpid;pid=fork();if(pid0)printf(error);elseif(pid=0)com=1;elsecom=com+1;printfcom=%dn,com;执行结果为:执行结果为:com=1com=1在在fork前父进程的资源子进程可前父进程的资源子进程可以继承,而在以继承,而在fork后子进程没有后子进程没有任何和父进程的继承关系了。任何和父进程的继承关系了。在子进程里创建的资源是子进程在子进程里创建的资源是子进程的,在父进程创建的资源是父进的,在父进程创建的资源是父进程的。可以完全看成两个进程。程的。可以完全看成两个进程。说
21、明:说明:关于进程ID号:PID通常在数值上逐渐增大,因此,子进程的PID一般比父进程大;当超过系统规定的最大值时,就返回来取尚未使用的最小值;如果父进程终止,则子进程会被指定给一个新的父进程。相应的系统调用:getpid() 返回当前进程的PIDgetppid() 返回父进程的PID#include;#include;main()pid_tpid;pid=fork();if(pid0)printf(“errorinfork!”);elseif(pid=0)printf(“iamthechildprocess,myprocessidis%dn”,getpid();elseprintf(“iam
22、theparentprocess,myprocessidis%dn”,getpid();运行的结果是:运行的结果是:应用示例应用示例2$a.outiamthechildprocess,myprocessidis1051iamtheparentprocess,myprocessidis1050$catfork_ex.c#include#includemain()pid_tpid;pid=fork();if(pid0)printf(forkerror!t);elseif(pid=0)printf(childprocessnow!pid=%dppid=%dt,getpid(),getppid();e
23、lseprintf(parentprocessnow!pid=%dchild_pid=%dt,getpid(),pid);设父进程设父进程PIDPID为为10501050,子进程的,子进程的PIDPID为为10511051,则输出为?,则输出为?childprocessnow!pid=1051ppid=1050parentprocessnow!pid=1050child_pid=1051#includeintmyvar=0;voidmain()pid_tpid;pid=fork();/systemcallif(pid0)/erroroccurredprintf(“forkfailed.”);e
24、xit(EXIT_FAILURE);/systemcallelseif(pid=0)/子进程被调度运行子进程被调度运行printf(“childprocessexecutingn”);myvar=1;else/parentprocesswait();/systemcall,waitforchildrencompletionprintf(“childcomplete.”);myvar+;printf(“father,myvar=%d”,myvar);exit(0);应用示例应用示例3fork与与wait系统调用的结合使用系统调用的结合使用1.#include2.#include3.#includ
25、e4.#include5.intmain(intargc,char*argv)6.7.pid_tpid,old_ppid,new_ppid;8.pid_tchild,parent;9.parent=getpid();10.if(child=fork()0)11.printf(“forkerror”);12.exit(1);13.表示有符号的整型变量使用系统调用获取本进程的PID使用系统调用创建子进程应用示例应用示例4fork于于sleep系统调用的系统调用的结合使用结合使用14.elseif(child=0)/*此时是子进程被调度运行此时是子进程被调度运行*/15.old_ppid=getpp
26、id();16.sleep(2);17.new_ppid=getppid();18.19.else20.sleep(1);21.exit(0);/*父进程退出,此时子进程父进程退出,此时子进程会被只派一个新的父进程会被只派一个新的父进程PID,值为,值为1*/14.15.printf(Originalparent:%dn,parent);16.printf(Child:%dn,getpid();17.printf(Childsoldppid:%dn,old_ppid);18.printf(Childsnewppid:%dn,new_ppid);19.exit(0);设运行时,父进程设运行时,父
27、进程PID和子进程和子进程PID分别为分别为1050、1051,则输出?,则输出?Originalparent:1050Child:1051Childsoldppid:1050Childsnewppid:1小结:u创建进程时需要使用系统调用fork 1. 子进程的创建原理 2. fork函数的独特之处 3. 程序中的使用方法u获取父进程和子进程的ID的系统调用 Linux下进程间通信的几种主要手段是:管道(pipe)信号(signal)消息队列(message)共享内存(shared memory)信号量(semaphore) 套接口(socket)7.4 进 程 通 信系统中的进程与内核之间
28、,以及不同的进程之间,需要进行协作,彼此间的通信十分重要。例例1:#include int pipe(int filedes2); 7.4.1 进程通信函数pipeLinux系统中,涉及进程通信的函数很多,既有系统调用,也有库函数。系统中,涉及进程通信的函数很多,既有系统调用,也有库函数。书本:书本:P215进程通信的函数和功能进程通信的函数和功能创建管道,其中参数创建管道,其中参数filedes存放两个整存放两个整数,分别代表管道的读和写端口。数,分别代表管道的读和写端口。即读和写对应的文件描述符。即读和写对应的文件描述符。管道文件管道(Pipe)是Linux中最常用的IPC机制,一个管道线
29、就是连接两个进程的一个打开文件, 例如:$ grep m?.c | wc l执行上述命令时,就要创建一个管道文件,和两个进程:1.命令grep m?.c对应一个进程,向管道文件中写入信息,称为“写进程”;2.命令wc l对应另一个进程,从管道文件中读取信息,称为“读进程”管道的实现机制每个进程最多可以打开每个进程最多可以打开256个文件,每个文件对应一个个文件,每个文件对应一个file结构。结构。f_inode表示表示VFS中该文件的索引结点。中该文件的索引结点。应用示例u下列代码简单地演示了: 用pipe函数创建管道, 然后用write将消息写入管道, 最后用read读取管道内容。1.#in
30、clude2.#include3.#include4.intmain(intargc,char*argv)5.6.staticconstcharmesg=HappyNewYear;7.charbufBUFSIZ;8.size_trcount,wcount;9.intp_fd2;10.size_tn;11.if(pipe(p_fd)0)12.printf(“pipeerror”);13.exit(EXIT_FAILURE);14.15.printf(Readend=fd%d,writeend=fd%dn,p_fd0,p_fd1);16.n=strlen(mesg);系统调用pipe函数,对整型数
31、组p_fd中的两个元素进行赋值,分别表示管道读端口和写端口的文件描述符。分别显示读和写的文件描述符应用示例应用示例1直接在一个进程中使用管道直接在一个进程中使用管道17.if(wcount=write(p_fd1,mesg,n)!=n)18.printf(“writeerror”);19.exit(EXIT_FAILURE);20.21.if(rcount=read(p_fd0,buf,BUFSIZ)!=wcount)22.printf(“readerror”);23.exit(EXIT_FAILURE);24.25.printf(Readfrompipen,buf);26.close(p_f
32、d0);27.close(p_fd1);28.return0;29.将缓存mesg中的数据写入文件描述符为 p_fd1 的文件中从文件描述符为 p_fd0 的文件中读出数据,并写入缓存buf中。运行结果为:运行结果为:Readend=fd3,writeend=fd4Readfrompipe#includeintmain()intfields2,charlen;charbuffer80;pid_tpid;pipe(fields);if(pid=fork()0)perror(“forkerror”);return-1;elseif(pid=0)chars=Hello!n;charlen=write
33、(fields1,s,sizeof(s);/将字符写入管将字符写入管道道elsewait(NULL);read(fields0,buffer,charlen);/从管道读取字符串从管道读取字符串printf(%s,buffer);应用示例应用示例2:pipe和和fork系统调用系统调用父子进程用管道进行通信父子进程用管道进行通信执行结果执行结果:Hello!从父进程到子进程的管道从父进程到子进程的管道wait和和waitpid函数的用法和区别函数的用法和区别wait使其调用者被阻塞,而使其调用者被阻塞,而waitpid有一选择项,有一选择项,可使调用者不阻塞。可使调用者不阻塞。waitpid函
34、数的调用者可以不用等待子进程终止,函数的调用者可以不用等待子进程终止,因为它有若干个选择项,可以选择子进程未结束因为它有若干个选择项,可以选择子进程未结束时直接返回到父进程,而不阻塞父进程。时直接返回到父进程,而不阻塞父进程。此外,可参考此外,可参考P212#include#include#includeintmain()pid_tpc,pr;if(pc=fork()0)printf(errorvforkn);elseif(pc=0)sleep(5);exit(0);elsewhile(pr=waitpid(pc,NULL,WNOHANG)=0)printf(“childprocessisnt
35、overn);sleep(1);if(pc=pr)printf(“childprocessisover);exit(0);文件:Fork_ex5.cintmain(void)pid_tchildpid;intstatus;if(childpid=fork()0)perror(fork();exit(EXIT_FAILURE);elseif(0=childpid)printf(Inchildprocess);sleep(3);printf(childpid=%dn,getpid();printf(childppid=%dn,getppid();exit(EXIT_SUCCESS);elsewai
36、tpid(childpid,&status,0);printf(inparentn);printf(parentpid=%dn,getpid();printf(childprocessexitedwithstatus%dn,status);exit(EXIT_SUCCESS);在给定的字符串后面显示上在给定的字符串后面显示上一个函数执行错误的原因一个函数执行错误的原因选项选项option为为0时则等时则等同于同于wait函数函数输出结果为?输出结果为?文件:Fork_ex6.crootlocalhostsrc#gccwaitpid.crootlocalhostsrc#./a.outInchil
37、dprocesschildpid=4469childppid=4468inparentparentpid=4468childprocessexitedwithstatus01 1信号概念信号概念信号(signal,称为软中断)机制:是在软件层次上对中断机制的一种模拟。异步进程可以通过彼此发送信号来实现简单通信。1.接收信号的进程,在运行过程中检测自身是否收到信号;2.若收到信号,则转去执行预先规定好的信号处理程序;3.再返回原先正在执行的进程。7.4.2 使用信号机制进行进程通信可采取以下处理方式:(1)由进程处理该信号:进程本身可在系统中标明处理信号的处理程序的地址,当收到该信号时,就由标明
38、的处理程序进行处理。(2)由系统进行默认处理:系统内核对各种信号都规定了相应的处理程序,在默认情况下,信号就由内核处理。进程间可用系统提供的系统调用发送信号;除了内核和超级用户之外,并不是每个进程都可以向其他进程发送信号;普通进程只能向具有相同uid和gid的进程发送信号。ukill 函数格式和功能 #include#includeintkill(pid_tpidintsig);函数说明:kill()可以用来送参数 sig 指定的信号给参数 pid 指定的进程。执行成功则返回 0,否则返回-1。应用示例应用示例3使用使用kill函数在进程中传递信号函数在进程中传递信号Kill函数的使用函数的使
39、用进程的进程的“软中断软中断”通信通信实现方式是:u一个进程通过系统调用kill(pid,sig) 向其它进程pid发送一个软中断信号;u另一进程通过系统调用signal(sig,func)捕捉到信号sig后,执行予先约定的动作func,从而实现这两个进程间的通信。发送信号kill(pid,sig)将指定信号sig(不一定是杀死子进程,可以是其他信号!)发送给指定进程pid,其中参数为pid进程号,pid与sig均为整数值。接收信号signal(sig,func)接收到其它进程发送给它的信号后,完成指定的功能func,func一般是函数。intmain()pid_tchild;intstatu
40、s,retval;if(child=fork()0)printf(“Parent:Signal17issenttoChild!n”);kill(child,17);wait();printf(“Parent:finished!n”);elseif(child=0)sleep(10);printf(“Child:AsignalfromParentisreceived!n”)exit(0);func()printf(“Itissignal17processingfunction!n”;)指出信号指出信号17的处理方式为的处理方式为函数函数func规定的功能。规定的功能。执行结果如下:执行结果如下:
41、Parent:Signal17issenttoChild!Itissignal17processingfunction!Child:AsignalfrommyParentisreceived!Parent:finished!KILL函数发出的消息不一定是终止进程,如:P220例7.7v消息队列的创建、写入和读出注:消息队列是消息的链接列表,具体的消息被保存在内核中。对消息队列的操作过程,通常是:1.用msgget函数创建一个新队列,或者打开一个已有队列;2.用msgsnd函数把一个新的消息添加到队列的末尾;3.用msgrcv从队列中读取一条消息。7.4.3 消息队列创建消息队列:创建消息队列:
42、intmsgget(key_tkey,intflags);若执行失败,则返回若执行失败,则返回-1发送消息:发送消息:intmsgsnd(intmsqid,structmsgbuf*ptr,size_tnbytes,intflags);接收消息:接收消息:intmsgrcv(intmsqid,structmsgbuf*ptr,size_tnbytes,longtype,intflags);消息队列的创建和读写函数消息队列的创建和读写函数详见详见P218,表表7.3所需包含的头文件是一样的所需包含的头文件是一样的#include#include#include应用示例应用示例4:书本书本P218
43、#defineBUFSZ512structmsg/*消息结构消息结构*/longtype;chartextBUFSZ;main()intqid;/*消息队列的消息队列的ID号号*/key_tkey;/*消息队列的键值消息队列的键值*/intlen1,len2;structmsgpmg_w,pmsg_r;/*创建消息队列创建消息队列*/qid=msgget(IPC_PRIVATE,ICP_CREAT|0666);if(qid0)perror(“msgget:create”);exit(EXIT_FAILURE);创建一个新的消息队列,创建一个新的消息队列,且访问权限为所有进程都且访问权限为所有进
44、程都可以访问可以访问/*从屏幕上获取将要发送的消息从屏幕上获取将要发送的消息*/puts(“entermessagetopost”);if(fgets(pmsg_w.text.BUFSZ,stdin)=NULL)puts(“wrong:nomessagetopost”);exit(EXIT_FAILURE);/*把消息加入到队列中把消息加入到队列中*/pmsg_w.text.type=10;len1=strlen(pmsg_w.text);if(msgsnd(qid,&pmsg_w,len1,IPC_NOWAIT)0)pmsgr._textlen2=0;printf(“readingqueue
45、id=%05dn”,qid);printf(“messagetype=%05dn”,pmsg_r.type);printf(“messagetext=%sn”,pmsg_r.text);elseperror(“megrcv”);exit(EXIT_FAILURE);exit(EXIT_SUCCESS);表示在消息队列呈空时,表示在消息队列呈空时,不做等待马上返回不做等待马上返回-1若接收的消息比若接收的消息比BUFSZ大,则按大,则按BUFSZ的大小的大小截断消息,并且不通知调截断消息,并且不通知调用进程。用进程。structmsgbuflongmsgtype;charmsgtext20;sn
46、dmsg,rcvmsg;intmain()key_tkey=IPC_PRIVATE;pid_tpid;intmsgid=0;intstatus;charstr1=testmessage:hello!;应用示例应用示例5:使用消息队列在使用消息队列在父子进程之间传递信息父子进程之间传递信息变量声明存放消息的缓存PART1仔细阅读下述程序,找出其中的一处逻辑错误仔细阅读下述程序,找出其中的一处逻辑错误if(pid=fork()0)printf(“forkerror”);exit(1);elseif(pid=0)if(msgidmsgid=msgget(key,IPC_CREAT|0666)0)pr
47、intf(childprocessmsggeterror);exit(EXIT_FAILURE);sndmsg.msgtype=10;strcpy(sndmsg.msgtext,str1);if(msgsnd(msgidmsgid,&sndmsg,strlen(str1),IPC_NOWAIT)0)printf(“childprocessmsgsnderror!n);exit(EXIT_FAILURE);elseprintf(“childprocesspostmessageOK”);1.打开描述字为打开描述字为key的消息队列,的消息队列,如果不存在则创建,并且访问权如果不存在则创建,并且访问
48、权限为所有进程都可以访问。限为所有进程都可以访问。2.若成功则返回队列编号,否则若成功则返回队列编号,否则返回返回-1PART2elsesleep(10);if(status=msgrcv(msgid,&rcvmsg,20,10,IPC_NOWAIT)0)printf(msgrcverror!n);exit(EXIT_FAILURE);printf(Thereceivedmessage:%s.n,rcvmsg.msgtext);return0;PART3从从msgid代表的队列中读取一个长度为代表的队列中读取一个长度为20字节的消息,将其存储在字节的消息,将其存储在rcvmsg指指向的结构中;
49、向的结构中;读取的消息类读取的消息类型为型为1010上述程序中的逻辑错误if(msgidmsgid=msgget(key,IPC_CREAT|0666)0)该语句应该放在创建子进程的fork()函数之前,否则在父进程里面,不能正确引用变量msgid的值!本章知识的延伸v课外阅读:边干边学:Linux内核指导 浙江大学出版社UNIX环境高级编程机械工业出版社本章作业本章作业按要求编写程序,实现下列shell命令的功能: $ cat m1.c | wc c假设m1.c文件中的内容不超过20个字节。要求:要求:1.写出包括头文件在内的完整程序代码;2.使用fork函数;3.分别用管道机制和消息队列的方式实现; (即:分别写出两个C语言程序)