【教学课件】第四章LINUX进程控制.ppt
《【教学课件】第四章LINUX进程控制.ppt》由会员分享,可在线阅读,更多相关《【教学课件】第四章LINUX进程控制.ppt(87页珍藏版)》请在三一办公上搜索。
1、第四章 LINUX进程控制,UNIX进程简介,进程的定义:进程是可并发执行的程序在一个数据集合上的运行过程。进程的概念理解的核心:运行过程的理解,包括运行过程中的不同状态的理解。正确理解进程和程序的关系。在UNIX中,每个进程在创建时都会被分配一个数据结构,称为进程控制块(简称PCB)。PCB中包含了很多重要的信息,其中最重要的进程ID(process ID)了,在我们最常使用的X86架构上,其变化范围是0-32767一个或多个进程可以合起来构成一个进程组(process group),一个或多个进程组可以合起来构成一个会话(session)。,进程号,#include/*提供类型pid_t的
2、定义*/#include/*提供函数的定义*/pid_t getpid(void);/*获得调用进程的ID号*/pid_t getppid(void);/*获得调用者父进程的ID号*/,程序的标识是程序名或文件名;进程的标识就是进程号,进程号建立了进程和用户之间的联系。,LINUX用户标识,相应的每一个用户也有一个用户ID.通过系统调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是进行保护的,为了获取一定资源进程还有一个有效用户ID.这个ID和系统的资源使用有关,涉及到进程的权限.通过系统调用geteuid我们可以得到进程的有效用户ID.和用户ID相对
3、应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效组ID,创建子进程fork,fork()系统调用创建一个新的进程,叫子进程,是调用进程的一个复制品.调用进程叫父进程。返回值:调用成功则对子进程返回0,对父进程返回子进程号,这也是最方便的区分父子进程的方法.若调用失败则返回-1给父进程,不生成子进程.,Fork继承信息,fork()的子进程继承了父进程的几乎所有的属 性,包括:进程空间及其内容实际UID,GID和有效UID,GID,附加GID环境变量.调用exec()时的关闭标志.UID设置模式比特位.GID设置模式比特位.进程组号,会话ID,控制终端
4、.当前工作目录,根目录.文件创建掩码UMASK.文件长度限制ULIMIT.预定值,如优先级和任何其他的进程预定参数,根据种类不同决定是否可以继承.还有一些其它属性.,Fork后子进程的不同信息,子进程也有与父进程不同的属性包括:进程号,子进程号不同与任何一个活动的进程组号.父进程号.子进程继承父进程的文件描述符或流时,具有自己的一个拷贝并且与父进程和其它子进程共享该资源.子进程的用户时间和系统时间被初始化为0.子进程的超时时钟设置为0.子进程的信号处理函数指针组置为空.子进程不继承父进程的记录锁.,关于fork的说明,在Linux中,创造新进程的方法只有一个,就是fork。其他一些库函数,如s
5、ystem(),实际上也在内部调用了fork。fork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM。示例参考正文4.3,Copy-On-Write的技术介绍,fork的实现并不做一个父进程数据段和堆的完全拷贝,因为在fork之后经常跟随着exec。作为替代,使用了在写时复制(Copy-On-Write,COW)的技术(注意不包括栈空间)。这些区域由父、子进程共享,fork时内核将它们的存取许可权改变为只读。如果有进程试图修改这些区域,则内核为有关部分,典型的是虚存系统中的“页”
6、,做一个拷贝。,Exec替换进程映象,第一个区别是前四个取路径名作为参数,后两个则取文件名作为参数。当指定filename作为参数时:如果filename中包含/,则就将其视为路径名;否则就按PATH环境变量,在有关目录中搜寻可执行文件第二个区别与参数表的传递有关(l表示列表,v表示矢量)。函数execl、execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。对于另外三个函数(execve,execvp和execve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数。最后一个区别与向新程序传递环境表相关。以e结尾的两个函
7、数(execle和execve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ变量为新程序复制现存的环境。,#include int execl(const char*path,const char*arg,.);int execv(const char*path,char*const argv);int execle(const char*path,const char*arg,.,char*const envp);int execve(const char*path,char*const argv,char*const envp);int execl
8、p(const char*file,const char*arg,.);int execvp(const char*file,char*const argv);,Exec的六个函数差异,这六个exec函数的参数很难记忆。函数名中的字符会给我们一些帮助。字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件。字母l表示该函数取一个参数列表,它与字母v互斥。v表示该函数取一个参数数组argv。最后,字母e表示该函数取e nvp 数组,而不使用当前环境。下图显示了这六个函数之间的区别,Exec函数使用说明(一),Exec的基本实现思想exec新进程还继承原进程的如下属性:附
9、加GID、进程号、父进程号、进程组号、会话号、控制终端、alarm时钟信号剩下的时间、当前工作目录、根目录、文件创建掩码、资源限制、用户时间、系统时间、子进程用户时间、子进程系统时间、记录锁、进程信号掩码、信号屏蔽、优先级、预定值.在调用这些系统调用前打开的文件指针对新进程来说也是打开的,除非它已定义了close-on-exec标志.打开的文件指针在新进程中保持不变,所有相关的文件锁也被保留.调用进程设置并正被捕俘的信号在新进程中被恢复为缺省设置,其它的则保持不变.新进程启动时按文件的SUID和SGID设置定义文件的UID和GID为有效UID和GID.,Exec函数使用说明(二),在exec前
10、后实际用户ID和实际组ID保持不变,而有效ID是否改变则取决于所执行程序的文件的设置-用户-ID位和设置-组-ID位是否设置。如果新程序的该位已设置,则有效用户ID变成程序文件所有者的ID,否则有效用户ID不变。对组ID的处理方式与此相同。例:编译核心代码为下面的程序,形成目标代码a.out printf(uid=%d,euid=%dn,getuid(),geteuid();$id查看当前用户uid和giduid=1028(zjt)gid=1028(zjt)groups=1028(zjt)$ls l a.out;a.out;执行程序,显示与id命令一样的结果-rwxrwxr-x 1 zjt z
11、jt 11585 Mar 25 11:38 a.outuid=1028,euid=1028$su root;进入根用户#chmod+s a.out;chown root a.out;exit改变用户执行时uid和gid,设置用户的owner为根并退出根用户$ls l a.out重新显示程序执行权限-rwsrwsr-x 1 root zjt 11585 Mar 25 11:38 a.out$a.out 执行程序,发现程序的euid已经改变uid=1028,euid=0,Exec函数使用说明(三),Linux调用exec的两个时机每当有进程认为自己不能为系统和用户做出任何贡献了,就可以调用任何一个
12、exec;或者一个进程想执行另一个程序,它就可以fork出一个新进程,然后立即调用任何一个exec,这样看起来就好像通过执行应用程序而产生了一个新进程一样。一般,fork后立即调用exec,可以最少限度减少空间复制操作。常见错误找不到文件或路径,此时errno被设置为ENOENT;数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;没有对要执行文件的运行权限,此时errno被设置为EACCES。实例4.9,system,system将参数string传递给一个命令解释器(一般为/bin/sh)执行,即string被解释为一条命令,由sh执行该命令.若参数string为
13、一个空指针则为检查命令解释器是否存在。该命令可以同命令行命令相同形式,但由于命令做为一个参数放在系统调用中,应注意编译时对特殊意义字符的处理.命令的查找是按PATH环境变量的定义的,命令所生成的后果一般不会对父进程造成影响.命令执行期间,SIGCHLD信号被阻塞,SIGINT和 SIGQUIT信号被忽略返回值:当参数为空指针时,只有当命令解释器有效时返回值为非零.若参数不为空指针,返回值为该命令的返回状态(同waitpid()的返回值.命令无效或语法错误则返回非零值,所执行的命令被终止.其他情况则返回-1.注:system接口的本质是fork+exec,但其执行的开销更大,#include i
14、nt system(const char*string);,atexit(),按照ANSI C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序(exit handler),并用atexit函数来登记这些函数。其中,atexit的参数是一个函数地址,当调用此函数时无需向它传送任何参数,也不期望它返回一个值。e x i t以登记这些函数的相反顺序调用它们(类似于压栈和弹栈方式)。同一函数如若登记多次,则也被调用多次。终止处理程序这一机制由ANSI C最新引进。SVR4和4.3+BSD都提供这种机制,LINUX同样支持。根据ANSI C和POSIX.
15、1,exit首先调用各终止处理程序,然后按需多次调用fclose,关闭所有打开流。,#include int atexit(void(*f u n c)(v o i d);返回:若成功则为0,若出错则为非0,atexit示例,$a.o u tmain is donefirst exit handlerfirst exit handlersecond exit handler,exit和_exit,两者的作用都是用来终止当前进程。无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩下的所有操作,清除包括PCB在内的各种数据结构,终止本进程的运行。,和exit比较一下,exit()函
16、数定义在stdlib.h中,而_exit()定义在unistd.h中,其功能差别主要体现在两点:exit按后进先出的顺序依次调用atexit()登记的函数并执行检查文件的打开情况,把文件缓冲区中的内容写回文件,exit和_exit在Linux函数库中的原型是:#includevoid exit(int status);#includevoid _exit(int status);,为何?exit对程序的影响,在Linux的标准函数库中,有一套称作“高级I/O”的函数:printf()、fopen()、fread()、fwrite(),它们也被称作“缓冲I/O(buffered I/O)”,其特
17、征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符n和文件结束符EOF),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。,实例:,第一个输
18、出:output begincontent in buffer 第二个输出:output begin注:由于缓冲的大小不同和系统设置的刷新值不同,输出信息多少可能会有差异,/*2.c*/#includemain()printf(output beginn);printf(content in buffer);_exit(0);,/*1.c*/#includemain()printf(output beginn);printf(content in buffer);exit(0);,典型C进程终止状态图,下图显示了一个C程序是如何起动的,以及它终止的各种方式,进程的僵尸体状态,事实上,在一个进程
19、调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构。在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何进程空间。从这点来看,僵尸进程对系统没有影响。示例 僵尸进程的由来:LINUX中僵尸进程的概念也是从传统UNIX上继承而来僵尸进程的作用僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,包括:这个进程是怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程强迫退出的?这个进
20、程占用的总系统CPU时间和总用户CPU时间分别是多少?发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与之相关的信息都立刻归于无形,而此时程序员或系统管理员需要用到,就没有办法了 问题:由于系统中的主存空间一定,如果系统中僵尸进程一直驻留的的话,那么,系统资源将很快耗尽(特别是早期计算机系统中内存都很小)!UNIX系统中怎样解决此问题?,/*zombie.c*/#include#include main()pid_t pid;pid=fork();if(pid0)/*如果出错*/printf(error occurred!n);else i
21、f(pid=0)/*如果是子进程*/exit(0);else/*如果是父进程*/sleep(60);/*休眠60秒,这段时间里,父进程什么也干不了*/wait(NULL);/*收集僵尸进程*/,$cc zombie.c-o zombie$./zombie&1 1577$ps-ax 1177 pts/0 S 0:00-bash 1577 pts/0 S 0:00./zombie 1578 pts/0 Z 0:00 zombie 1579 pts/0 R 0:00 ps ax说明:进程状态一栏中间的Z状态就是僵尸进程的标志,它表示1578号进程现在就是一个僵尸进程,示例,Wait,Wait的作用是
22、等待子进程结束,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,我们就可以设定这个参数为NULL,如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。用wait的功能我们可以实现父
23、进程和子进程之间的同步,如:父进程的运算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执行的先决条件(如:子进程建立文件,而父进程写入数据),此时父进程就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之间的同步,更准确地说,这是进程同步的一种特例。进程回收的两个重要提示:Shell负责回收所有前台进程Init进程负责回收所有后台进程和其它用户未回收的进程,#include/*提供类型pid_t的定义*/#include pid_t wait(int*status),Wait中的status参数,如果参数status的值不是NULL,wait就会把子进程退出时的状态取
24、出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏来完成这项工作,下面介绍其中最常用的两个:WIFEXITED(status)这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。注意:虽然名字一样,这里的参数status并不同于wait唯一的参数-指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。)WEXITSTATUS(status)当W
25、IFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。,waitpid,从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。参数pid定义:pid0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 教学课件 教学 课件 第四 LINUX 进程 控制

链接地址:https://www.31ppt.com/p-5664835.html