并行的环境课题组-工作汇报-课件_第1页
并行的环境课题组-工作汇报-课件_第2页
并行的环境课题组-工作汇报-课件_第3页
并行的环境课题组-工作汇报-课件_第4页
并行的环境课题组-工作汇报-课件_第5页
已阅读5页,还剩82页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章 LINUX进程控制 第四章 LINUX进程控制 UNIX进程简介 进程的定义:进程是可并发执行的程序在一个数据集合上的运行过程。进程的概念理解的核心:运行过程的理解,包括运行过程中的不同状态的理解。正确理解进程和程序的关系。在UNIX中,每个进程在创建时都会被分配一个数据结构,称为进程控制块(简称PCB)。PCB中包含了很多重要的信息,其中最重要的进程ID(process ID)了,在我们最常使用的X86架构上,其变化范围是0-32767一个或多个进程可以合起来构成一个进程组(process group),一个或多个进程组可以合起来构成一个会话(session)。 UNIX进程简介 进

2、程的定义:进程是可并发执行的程序在一个数进程号#include /* 提供类型pid_t的定义*/#include /* 提供函数的定义 */pid_t getpid(void); /*获得调用进程的ID号*/pid_t getppid(void); /*获得调用者父进程的ID号*/程序的标识是程序名或文件名;进程的标识就是进程号,进程号建立了进程和用户之间的联系。进程号#include /* 提供LINUX用户标识相应的每一个用户也有一个用户ID.通过系统调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是进行保护的,为了获取一定资源进程还有一个有效用

3、户ID.这个ID和系统的资源使用有关,涉及到进程的权限. 通过系统调用geteuid我们可以得到进程的有效用户ID. 和用户ID相对应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效组IDLINUX用户标识相应的每一个用户也有一个用户ID.通过系创建子进程fork fork()系统调用创建一个新的进程, 叫子进程, 是调用进程的一个复制品. 调用进程叫父进程。返回值: 调用成功则对子进程返回0, 对父进程返回子进程号, 这也是最方便的区分父子进程的方法. 若调用失败则返回-1给父进程,不生成子进程. 创建子进程fork fork()系统调用创建一个新的

4、进程Fork继承信息fork()的子进程继承了父进程的几乎所有的属 性,包括:进程空间及其内容实际UID,GID和有效UID,GID,附加GID环境变量.调用exec()时的关闭标志.UID设置模式比特位.GID设置模式比特位.进程组号,会话ID,控制终端.当前工作目录,根目录.文件创建掩码UMASK.文件长度限制ULIMIT.预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承.还有一些其它属性. Fork继承信息fork()的子进程继承了父进程的几乎所Fork后子进程的不同信息 子进程也有与父进程不同的属性包括:进程号, 子进程号不同与任何一个活动的进程组号.父进程号

5、.子进程继承父进程的文件描述符或流时, 具有自己的一个拷贝并且与父进程和其它子进程共享该资源.子进程的用户时间和系统时间被初始化为0.子进程的超时时钟设置为0.子进程的信号处理函数指针组置为空.子进程不继承父进程的记录锁. Fork后子进程的不同信息 子进程也有与父进程不同的属性包括关于fork的说明 在Linux中,创造新进程的方法只有一个,就是fork。其他一些库函数,如system(),实际上也在内部调用了fork。 fork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM。

6、 示例参考正文4.3关于fork的说明 在Linux中,创造新进程的方法只有一个Copy-On-Write的技术介绍fork的实现并不做一个父进程数据段和堆的完全拷贝,因为在fork之后经常跟随着exec。作为替代,使用了在写时复制( Copy-On-Write, COW)的技术(注意不包括栈空间) 。这些区域由父、子进程共享,fork时内核将它们的存取许可权改变为只读。如果有进程试图修改这些区域,则内核为有关部分,典型的是虚存系统中的“页”,做一个拷贝。Copy-On-Write的技术介绍fork的实现并不做一个Exec替换进程映象第一个区别是前四个取路径名作为参数,后两个则取文件名作为参数

7、。当指定filename作为参数时:如果filename中包含/,则就将其视为路径名;否则就按PATH环境变量,在有关目录中搜寻可执行文件第二个区别与参数表的传递有关( l表示列表,v表示矢量)。函数execl、execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。对于另外三个函数(execve,execvp和execve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数。最后一个区别与向新程序传递环境表相关。以e结尾的两个函数(execle和execve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用

8、进程中的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 execlp(const char *file, const ch

9、ar *arg, .);int execvp(const char *file, char *const argv);Exec替换进程映象第一个区别是前四个取路径名作为参数,后Exec的六个函数差异 这六个exec函数的参数很难记忆。函数名中的字符会给我们一些帮助。字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件。字母l表示该函数取一个参数列表,它与字母v互斥。v表示该函数取一个参数数组argv 。最后,字母e表示该函数取e nvp 数组,而不使用当前环境。下图显示了这六个函数之间的区别Exec的六个函数差异 这六个exec函数的参数很难记忆。Exec函数使用说

10、明(一)Exec的基本实现思想exec新进程还继承原进程的如下属性:附加GID、进程号、父进程号、进程组号、会话号、控制终端、alarm时钟信号剩下的时间、当前工作目录、根目录、文件创建掩码、资源限制、用户时间、系统时间、子进程用户时间、子进程系统时间、记录锁、进程信号掩码、信号屏蔽、 优先级、预定值.在调用这些系统调用前打开的文件指针对新进程来说也是打开的,除非它已定义了close-on-exec标志.打开的文件指针在新进程中保持不变,所有相关的文件锁也被保留.调用进程设置并正被捕俘的信号在新进程中被恢复为缺省设置,其它的则保持不变.新进程启动时按文件的SUID和SGID设置定义文件的UID

11、和GID为有效UID和GID. Exec函数使用说明(一)Exec的基本实现思想Exec函数使用说明(二)在exec前后实际用户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=

12、1028(zjt)$ ls l a.out;a.out;执行程序,显示与id命令一样的结果-rwxrwxr-x 1 zjt zjt 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=

13、0 Exec函数使用说明(二)在exec前后实际用户ID和实际组Exec函数使用说明(三)Linux调用exec的两个时机每当有进程认为自己不能为系统和用户做出任何贡献了,就可以调用任何一个exec;或者一个进程想执行另一个程序,它就可以fork出一个新进程,然后立即调用任何一个exec,这样看起来就好像通过执行应用程序而产生了一个新进程一样。一般,fork后立即调用exec,可以最少限度减少空间复制操作。常见错误找不到文件或路径,此时errno被设置为ENOENT;数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;没有对要执行文件的运行权限,此时errno被设置为

14、EACCES。 实例4.9Exec函数使用说明(三)Linux调用exec的两个时机systemsystem将参数string传递给一个命令解释器(一般为/bin/sh)执行, 即string被解释为一条命令, 由sh执行该命令.若参数string为一个空指针则为检查命令解释器是否存在。该命令可以同命令行命令相同形式, 但由于命令做为一个参数放在系统调用中, 应注意编译时对特殊意义字符的处理. 命令的查找是按PATH环境变量的定义的,命令所生成的后果一般不会对父进程造成影响.命令执行期间,SIGCHLD信号被阻塞, SIGINT和 SIGQUIT信号被忽略返回值:当参数为空指针时, 只有当命令

15、解释器有效时返回值为非零.若参数不为空指针, 返回值为该命令的返回状态(同waitpid()的返回值. 命令无效或语法错误则返回非零值,所执行的命令被终止. 其他情况则返回-1. 注:system接口的本质是fork+exec,但其执行的开销更大#include int system(const char *string);systemsystem将参数string传递给一个命令解释atexit()按照ANSI C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序(exit handler),并用atexit函数来登记这些函数。其中, atexi

16、t的参数是一个函数地址,当调用此函数时无需向它传送任何参数,也不期望它返回一个值。e x i t以登记这些函数的相反顺序调用它们(类似于压栈和弹栈方式)。同一函数如若登记多次,则也被调用多次。终止处理程序这一机制由ANSI C最新引进。SVR4和4.3+BSD都提供这种机制,LINUX同样支持。根据ANSI C和POSIX.1,exit首先调用各终止处理程序,然后按需多次调用fclose,关闭所有打开流。#include int atexit(void (* f u n c) ( v o i d ) ) ;返回:若成功则为0,若出错则为非0atexit()按照ANSI C的规定,一个进程可以登

17、记多atexit示例$ a . o u tmain is donefirst exit handlerfirst exit handlersecond exit handleratexit示例$ a . o u texit和_exit 两者的作用都是用来终止当前进程。无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩下的所有操作,清除包括PCB在内的各种数据结构,终止本进程的运行。和exit比较一下,exit()函数定义在stdlib.h中,而_exit()定义在unistd.h中,其功能差别主要体现在两点:exit按后进先出的顺序依次调用atexit()登记的函数并执行检查文

18、件的打开情况,把文件缓冲区中的内容写回文件exit和_exit在Linux函数库中的原型是:#includevoid exit(int status);#includevoid _exit(int status); exit和_exit 两者的作用都是用来终止当前进程。无论为何?exit对程序的影响 在Linux的标准函数库中,有一套称作“高级I/O”的函数:printf()、fopen()、fread()、fwrite(),它们也被称作“缓冲I/O(buffered I/O)”,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接

19、从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符n和文件结束符EOF),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。为何?exit对程序的影响 在Linux的标准函数库中实例: 第一个输出:output begincontent in buff

20、er 第二个输出: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); 实例: 第一个输出:/* 2.c */* 1.c */典型C进程终止状态图 下图显示了一个C程序是如何起动的,以及它终止的各种方式 典型C进程终止状态图 下图显示了一

21、个C程序是如何起动的,以及进程的僵尸体状态事实上,在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构。在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何进程空间。从这点来看,僵尸进程对系统没有影响。示例 僵尸进程的由来:LINUX中僵尸进程的概念也是从传统UNIX上继承而来僵尸进程的作用僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,包括:这个进程是怎么死亡的?是

22、正常退出呢,还是出现了错误,还是被其它进程强迫退出的?这个进程占用的总系统CPU时间和总用户CPU时间分别是多少?发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与之相关的信息都立刻归于无形,而此时程序员或系统管理员需要用到,就没有办法了 问题:由于系统中的主存空间一定,如果系统中僵尸进程一直驻留的的话,那么,系统资源将很快耗尽(特别是早期计算机系统中内存都很小)!UNIX系统中怎样解决此问题?进程的僵尸体状态事实上,在一个进程调用了exit之后,该进程 /* zombie.c */#include #include main()pid_t

23、 pid;pid=fork();if(pid0)/* 如果出错 */printf(error occurred!n);else if(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 157

24、9 pts/0 R 0:00 ps ax说明:进程状态一栏中间的Z状态就是僵尸进程的标志,它表示1578号进程现在就是一个僵尸进程 示例 /* zombie.c */$ cc zombie.c -WaitWait的作用是等待子进程结束,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸

25、进程消灭掉,我们就可以设定这个参数为NULL,如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。 用wait的功能我们可以实现父进程和子进程之间的同步,如:父进程的运算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执行的先决条件(如:子进程建立文件,而父进程写入数据),此时父进程就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之间的同步,更准确地说,这是进程同步的一种特例。进程回收的两个重要提示:Shell负责回收所有前台进程Init进程负责回收所有后台进程和其它用户未回收

26、的进程#include /* 提供类型pid_t的定义 */#include pid_t wait(int *status) WaitWait的作用是等待子进程结束,由wait自动分析是Wait中的status参数 如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏来完成这项工作,下面介绍其中最常用的两

27、个:WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。注意:虽然名字一样,这里的参数status并不同于wait唯一的参数-指向整数的指针status,而是那个指针所指向的整数,切记不要搞混了。)WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就

28、毫无意义。 Wait中的status参数 如果参数status的值waitpid 从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。参数pid定义:pid0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。 pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。 pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,wai

29、tpid不会对它做任何理睬。pid-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。#include /* 提供类型pid_t的定义*/#include pid_t waitpid(pid_t pid,int *status,int options) waitpid 从本质上讲,系统调用waitpid和waiwaitpid 参数options定义: options提供了一些额外的选项来控制waitpid, Linux中主要的有WNOHANG和WUNTRACED两个选项 WNOHANG:指示即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。可用于在程序

30、中插入检测。WUNTRACED:指示子进程被跟踪停止时也返回,用于实现调试控制。 waitpid的返回值比wait稍微复杂一些,一共有3种情况:当正常返回的时候,waitpid返回收集到的子进程的进程ID; 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0; 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在; 当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD; #include /* 提供类型pid_t的定义*/#include pid_t wa

31、itpid(pid_t pid,int *status,int options) waitpid 参数options定义: #include 更改用户和组ID setuid函数设置实际用户ID和有效用户ID。setgid函数设置实际组ID和有效组ID ,两个函数返回:若成功则为0,若出错则为-1 。关于更改用户ID的若干规则(同样适用于组ID):1)若进程具有超级用户特权,则setuid函数可将实际用户ID、有效用户ID,以及保存的设置-用户-ID设置为uid参数值。2)若进程没有超级用户特权,但uid等于实际用户ID或保存的设置-用户-ID,则setuid只将有效用户ID设置为uid。不改变

32、实际用户ID和保存的设置-用户-ID。3)如果上面两个条件都不满足,则errno设置为EPERM,并返回出错。 #include #include int setuid(uid_t uid) ;int setgid(gid_t gid) ; 更改用户和组ID setuid函数设置实际用户ID和有效用关于设置用户和组ID的注意事项关于内核所维护的三个用户ID,还要注意下列几点:只有超级用户进程可以更改实际用户ID。通常,实际用户ID是在用户登录时,由login(1)程序设置的,而且决不会改变它。因为login是一个超级用户进程,当它调用setuid时,设置所有三个用户ID。仅当对程序文件设置了设

33、置-用户-ID位时,exec函数才改变有效用户ID。如果设置-用户-ID位没有设置,则exec函数不会改变有效用户ID,而将其维持为原先值。任何时候都可以调用setuid,将有效用户ID设置为实际用户ID或保存的设置-用户-ID。保存的设置-用户- I D是由e x e c从有效用户I D复制的。在e x e c按文件用户I D设置了有效用户I D后,即进行这种复制,并将此副本保存起来。下表列出了改变这三个用户I D的不同方法。关于设置用户和组ID的注意事项关于内核所维护的三个用户IDsetreuid/setregidseteuid/setegid Setreuid设置当前进程的实际和有效id

34、Setregid设置当前进程的实际和有效组idSeteuid设置有效用户IDSetegid设置有效用户组ID这些函数的设置原则与setuid基本一致说明:setuid/setgid的典型用法是根用户设置普通用户的uid/gid,以普通用户身份执行某些操作,如:创建用户临时文件等。一般在编写具setuid root的程序时,为减少此类程序带来的系统安全风险,在使用完root权限后建议马上执行setuid(getuid();来抛弃root权限。进程uid和euid不一致时Linux系统将不会产生core dump#include #include int setreuid(uid_t ruid,

35、uid_t euid) ;int setregid(gid_t rgid, gid_t egid) ; setreuid/setregidseteuid/sete用户ID转换关系图 用户ID转换关系图 取用户标识getlogin Getlogin()返回用户登陆名一般情况下,我们可以调用getpwuid(getuid(),得到用户名、真实姓名及HOME目录等信息。存在一种情况:如果一个用户有多个登录名,这些登录名又对应着同一个用户ID,那么又将如何呢?(一个人在口令文件中可以有多个登录项,它们的用户ID相同,但登录shell则不同。)系统通常保存用户的登录名,用getlogin函数可以存取此登

36、录名。如果调用此函数的进程没有连接到用户登录时所用的终端,则本函数会失败。用户得到了登录名,就可用getpwnam在口令文件中查找相应记录以确定其登录shell等。 #include char *getlogin(void);取用户标识getlogin Getlogin()返回用户登进程统计 参数非空时,acct打开系统记帐并把终止进程的信息追加到该文件中,参数空时,关闭系统记帐功能。LINUX系统提供了一个选择项以进行进程事务统计处理。当取了这种选择项后,每当进程结束时内核就写一个会计记录。典型的会计记录是3 2字节长的二进制数据,包括命令名、所使用的C P U时间总量、用户I D和组I D

37、、起动时间等。记帐记录所需的各个数据都由内核保存在进程表中,并在一个新进程被创建时置初值(例如fork之后在子进程中)。进程终止时写一个会计记录。这就意味着在记帐文件中记录的顺序对应于进程终止的顺序,而不是它们起动的顺序。为了确定起动顺序,需要读全部记帐文件,并按起动日历时间进行排序。这不是一种很完善的方法,因为日历时间的单位是秒,在一个给定的秒中可能起动了多个进程。记帐记录对应于进程而不是程序。在f o r k之后,内核为子进程初始化一个记录,而不是在一个新程序被执行时。虽然e x e c并不创建一个新的记帐记录,但相应记录中的命令名改变了,A F O R K标志则被清除。这意味着,如果一个

38、进程顺序执行了三个程序(A exec B,B exec C,最后C exit),但只写一个会计记录。在该记录中的命令名对应于程序C,但C P U时间是程序A、B、C之和。 #include int acct(const char *filename);进程统计 参数非空时,acct打开系统记帐并把终止进程的信进程执行时间 用户进程可以通过调用t i m e s函数获得它自己及终止子进程的时间值 。此函数填写由buf指向的tms结构,该结构定义如下:struct tms clock_t tms_utime; /* user time */ clock_t tms_stime; /* system

39、 time */ clock_t tms_cutime;/* user time of children */ clock_t tms_cstime; /* system time of children*/ ;结构中两个针对子进程的字段包含了此进程已等待到的各子进程的值由此函数返回的clock_t值都用_SC_CLK_TCK (由sysconf函数返回的每秒时钟滴答数)变换成秒数。 #include clock_t times(struct tms *buf);进程执行时间 用户进程可以通过调用t i m e s函数获小结 对在LINUX环境中的高级程序设计而言,完整地了解LINUX的进程控

40、制非常重要。其中必须熟练掌握的只有几个fork、exec族、_exit、wait和waitpid。通过对这些接口的理解,我们应该了解LINUX下新进程是怎么产生的?不同的程序又是如何执行的?以及进程控制的一些基本内容。对各种不同的用户I D和组I D (实际,有效和保存的)的理解和编写安全的设置-用户- I D程序是至关重要的。本章的最后,我们还介绍了LINUX下的几个辅助系统调用。小结 对在LINUX环境中的高级程序设计而言,完整地了解L第六章 LINUX下的信号 第六章 LINUX下的信号 信号的本质信号是在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中

41、断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。 信号的本质信号是在软件层次上是对中断机制的一种模拟,在原理上信号的来源信号事件的发生有两个来源:硬件来源硬件中断触发,如键盘其它硬件故障,如IO BUS error硬件触发的程序异常软件来源程序或命令控制的信号,如:kill, raise等系统闹钟程序调试程序异常,如:段违例、非法运算等操作

42、信号的来源信号事件的发生有两个来源:信号的种类可以从两个不同的分类角度对信号进行分类:可靠性方面:可靠信号与不可靠信号;与时间的关系上:实时信号与非实时信号。 信号的种类可以从两个不同的分类角度对信号进行分类:不可靠信号 Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做“不可靠信号”,信号值小于SIGRTMIN(Red hat LINUX中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是“不可靠信号”的来源。它的主要问题是:进程每次处理信号后,就将对

43、信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。信号可能丢失。早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。这种情况主要出现在:当信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。 不可靠信号 Linux信号机制基本上是从Unix系统中继承不可靠信号示例说明:这段代码段的一个问题是:在信号发生之后到信号处理程序中调用signal函数之间有一个时间窗口。在此段时间中,可能发生另一次中断信号。第二个信号会造成执行默认动作,而对中断信号

44、则是终止该进程。这种类型的程序段在大多数情况下会正常工作,使得我们认为它们正确,而实际上却并不是如此。Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。 不可靠信号示例说明:可靠信号 随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种Unix版本分别在这方面进行了研究,力图实现“可靠信号”。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持

45、排队,不会丢失。信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。POSIX对可靠信号机制做了标准化。但是,POSIX只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数和kill()函数。 可靠信号 随着时间的发展,实践证明了有必要对信号的原始机制加一点说明

46、可靠信号是指后来添加的新信号(信号值位于SIGRTMIN及SIGRTMAX之间);不可靠信号是信号值小于SIGRTMIN的信号。信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),

47、而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数(对所有信号这一点都成立),而经过signal安装的信号却不能向信号处理函数传递信息,对于信号发送函数来说也是一样的。 一点说明可靠信号是指后来添加的新信号(信号值位于SIGRTM实时信号和非实时信号 LINUX中信号的编号为0-63,将来可能进一步增加,这需要得到内核的支持。前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。后32个信号(SIGRTMIN=31,SIGRTMAX=63)表示实时信号,等同于前面阐述的可靠信号。

48、这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。 实时信号和非实时信号 LINUX中信号的编号为0-63,将来进程对信号的响应进程可以通过三种方式来响应一个信号:忽略信号(SIG_DFL),即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP; 捕捉信号 (CATCH)。定义信号处理函数,当信号发生时,执行相应的处理函数; 执行缺省操作(SIG_IGN),Linux对每种信号都规定了默认操作,详细情况请参考下表。注意,进程对实时信号的缺省反应是进程终止。Li

49、nux究竟采用上述三种方式的哪一个来响应信号,取决于传递给相应API函数的参数。 进程对信号的响应进程可以通过三种方式来响应一个信号:并行的环境课题组-工作汇报-课件关于上表的说明在系统默认动作列,“终止w / c o r e”表示在进程当前工作目录的产生了c o r e文件,该文件中记录了该进程非正常退出时的存储图像,大多数U N I X调试程序都使用c o r e文件以检查进程在终止时的状态。在下列条件下不产生c o r e文件:( a )进程是设置-用户-ID,而且当前用户并非程序文件的所有者( b )进程是设置-组-ID,而且当前用户并非该程序文件的组所有者( c )用户没有写当前工作

50、目录的许可权( d )文件太大。core文件的许可权(假定该文件在此之前并不存在)通常是用户读写,组读和其他读。 关于上表的说明在系统默认动作列,“终止w / c o r eLINUX信号列表(按序号排列) SIGHUP:如果终端界面检测到一个连接断开,则将此信号送给与该终端相关的控制进程(对话期首进程)。SIGINT:当用户按中断键(一般采用DELETE或Ctrl-C)时,终端驱动程序产生此信号并送至前台进程组中的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。SIGQUIT:当用户在终端上按退出键(一般采用Ctrl-)时,产生此信号,并送至前

51、台进程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件SIGILL:此信号指示进程已执行一条非法硬件指令SIGTRAP:指示一个实现定义的硬件故障,一般用于跟踪调试自陷SIGABRT:调用abort函数时产生此信号。进程异常终止SIGIOT:IOT自陷,指示一个实现定义的硬件故障SIGBUS:总线错,指示一个实现定义的硬件故障SIGFPE:浮点相关异常SIGKILL:这是两个不能被捕捉或忽略信号中的一个。它向系统管理员提供了一种可以杀死任一进程的可靠方法。SIGUSR1:用户自定义信号LINUX信号列表(按序号排列) SIGHUP:如果终端界面LI

52、NUX信号列表(续)SIGSEGV:存储访问相关的异常,一般为非法存储空间读写引起SIGUSR2:用户自定义信号SIGPIPE:如果在读进程已终止时写管道,则产生此信号。当套接口的一端已经终止时,若进程写该套接口也产生此信号SIGALRM:用alarm函数设置的时间到达时产生此信号。若由s e t i t i m e r ( 2 )函数设置的间隔时间已经过时,那么也产生此信号SIGTERM:终端信号,是由kill(1)命令发送的系统默认终止信号SIGSTKFLT:栈故障相关信号SIGCHLD:在一个进程终止或停止时,SIGCHLD信号被送给其父进程。按系统默认,将忽略此信号。如果父进程希望了解

53、其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要调用wait函数以取得子进程ID和其终止状态。SIGCONT:跟踪调试时的继续信号SIGSTOP:是一个作业控制信号,它停止一个进程,SIGSTOP不能被捕捉或忽略SIGTSTP:交互停止信号,当用户在终端上按挂起键(一般采用Ctrl-Z)时,终端驱动程序产生此信号。 LINUX信号列表(续)SIGSEGV:存储访问相关的异常,LINUX信号列表(续)SIGTTIN:当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号。在下列例外情形下不产生此信号,此时读操作返回出错, errno设置为EIO:(a)读进程忽略或阻塞此信号,

54、或(b)读进程所属的进程组是孤儿进程组。SIGTTOU:当一个后台进程组进程试图写其控制终端时产生此信号。与上面所述的SIGTTIN信号不同,一个进程可以选择为允许后台进程写控制终端。如果不允许后台进程写,则与SIGTTIN相似也有两种特殊情况:( a )写进程忽略或阻塞此信不幸的是,术语停止( s t o p )有不同的意义。在讨论作业控制和信号时我们需提及停止和继续作业。但是终端驱动程序一直用术语停止表示用C t r l - S和C t r l - Q字符停止和起动终端输出。因此,终端驱动程序将产生交互停止信号和字符称之为挂起字符而非停止字符。号,或( b )写进程所属进程组是孤儿进程组。

55、在这两种情况下不产生此信号,写操作返回出错,errno设置为E I O。SIGURG:该信号通知进程已经发生一个紧急情况。在网络连接上,接到非规定波特率的数据时,此信号可选择地产生SIGXCPU:如果进程超过了其软C P U时间限制,则产生此信号。SIGXFSZ:如果进程超过了其软文件长度限制,则产生此信号。LINUX信号列表(续)SIGTTIN:当一个后台进程组进程LINUX信号列表(续)SIGXFSZ:如果进程超过了其软文件长度限制,则产生此信号。SIGVTALRM:当一个由setitimer ( 2 )函数设置的虚拟间隔时间已经超过时产生此信号SIGPROF:当setitimer ( 2

56、 )函数设置的梗概统计间隔时间已经超过时产生此信号。SIGWINCH:如果一个进程用ioctl的设置-窗口-大小命令更改了窗口大小,则内核将SIGWINCH信号送至前台进程组。SIGPOLL:当在一个可轮询设备上发生一特定事件时产生此信号。SIGIO:该信号指示一个异步I / O事件。 LINUX信号列表(续)SIGXFSZ:如果进程超过了其软文LINUX信号列表(续)SIGPWR:这是一种SVR4信号,它依赖于系统,LINUX也支持。它主要用于具有不间断电源(UPS)的系统上,如果电源失效,则UPS起作用,而且通常软件会接到通知。在这种情况下,系统依靠蓄电池电源继续运行,所以无须作任何处理。

57、但是如果蓄电池也将不能支持工作,则软件通常会再次接到通知,此时,它在1 53 0秒内使系统各部分都停止运行。此时应当传递SIGPWR信号。在大多数系统中使接到蓄电池电压过低的进程将信号SIGPWR发送给init进程,然后由init处理停机操作。很多系统V的init实现在inittab文件中提供了两个记录项用于此种目的;powerfail以及powerwait。# When our UPS tells us power has failed, assume we have a few minutes# of power left. Schedule a shutdown for 2 minute

58、s from now.# This does, of course, assume you have powerd installed and your# UPS connected and working correctly.pf:powerfail:/sbin/shutdown -f -h +2 Power Failure; System Shutting Down# If power was restored before the shutdown kicked in, cancel it.pr:12345:powerokwait:/sbin/shutdown -c Power Rest

59、ored; Shutdown Cancelled由于目前已能获得低价格的U P S系统,它用RS - 232串行连接能够很容易地将蓄电池电压过低的条件通知系统,于是这种信号也就更加重要了。 LINUX信号列表(续)SIGPWR:这是一种SVR4信号,信号的发送 LINUX中发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort() 信号的发送 LINUX中发送信号的主要函数有:kill(kill signo是信号值,当为0时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,以及当前进

60、程是否具有向目标发送信号的权限(root权限的进程可以向任何进程发送信号,非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号) 调用成功返回 0; 否则,返回 -1。 #include #include kill(pid_t pid,int signo) 参数pid的值信号的接收进程pid0进程ID为pid的进程pid=0同一个进程组的进程pid0 pid!=-1进程组ID为 -pid的所有进程pid=-1除发送进程自身外,所有进程ID大于1的进程kill signo是信号值,当为0时(即空信号),raise向进程本身发送信号,参数为即将发送的信号值。调用成功返回

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论