[APUE]第八章 进程控制

第八章 进程控制

引言:

 

如何创建进程?执行程序?进程终止?

讲述进程属性的各种ID---实际、有效、保存的用户和ID,他们是如何受到进程控制原语的影响。

解释器文件和system函数,进程会计机制。

 

进程标识符

每一个进程都有一个非负整数表示的唯一进程ID,这个进程ID是唯一的,他的使用机制和文件描述符是不相同的。文件打开的都是最小的整数(未打开),进程ID是采用延迟重用算法。

init 0表示关机 init 6表示重新启动

ID位0的是一个交换进程,该进程是内核的一部分。

ID为1的是init进程

pid_t getpid() ;

pid_t getppid();

uid_t getuid();

uid_t geteuid();

gid_t getgid();

gid_t getegid();

 

fork

创建进程fork,返回两次,子进程返回0,父进程返回子进程的ID,

pid_t fork(); //出错返回-1

 

fork的一个特性是父进程的所有打开的文件描述符都被复制到子进程中,父子进程的每一个相同的打开文件描述符共享一个文件表项。重定向父进程对的标准输出的时候,子进程的标准输出也重定向。

 

子进程会复制父进程的数据空间。

 

子进程会继承父进程的

实际用户ID、有效用户ID、实际组ID、有效组ID

附加组ID

会话ID

控制终端

设置用户ID标志和设置组DI标志

当前工作目录、根目录

文件模式的创建屏蔽字

 

子进程不会继承父进程的

进程ID

父进程设置的文件锁

 

vfork

pid_t vfork()

在调用exec或者exit之前,子进程在父进程的地址空间运行,子进程修改的变量会修改父进程的值。还有一个特点是vfork保障子进程先运行,在他调用exec或者exit之后父进程才可能被调度运行。

 

exit函数

五种进程正常终止

1  main函数中return返回

                                     2       调用exit()

                                     3       调用_Exit()、_exit()

                                     4       最后一个线程返回

                                     5       最后一个线程调用phread_exit()

异常终止:

1          调用abort

2          进程收到某些信号,使进程退出

3          最后一个线程对取消做出反应 pthread_cancel()

 

不管进程如何终止,都会关闭文件描述符和释放他使用的存储器

 

退出状态:是return value、exit(value) 中的value

终止状态:是wait(int &value)中的value

 

关于父子进程谁先终止的问题

如果父进程先终止,那么子进程的父进程就变成init(pid是1)的进程,子进程终止的时候,init为它收尸。不会僵死占用空间。

如果子进程先终止,那么父进程必须为子进程收尸(wait),不然子进程就会一直占用空间,导致出现僵死进程。

 

子进程终止的时候,会向父进程发送SIGCHLD信号。这个时候可以对子进程进行处理。

wait和waitpid可能的情况

如果子进程正在运行,则阻塞

如果没有子进程就直接出错返回

如果子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态时立即返回。

[cpp]  view plain copy
  1. pid_t wait(int &status)  
  2. pid_t waitpid(pid_t pid, int *status, intoptions)  

如果pid == 0 等待其组ID等于调用进程组ID的任意子进程

         pid== -1 等待任意子进程

         pid》0 等待pid位pid的子进程

         pid《0 等待pid为|pid|的子进程。

 

exec*函数

[cpp]  view plain copy
  1. #include <unistd.h>  
  2. int execl(const char *path, const char*arg, ...);  
  3. int execlp(const char *file, const char*arg, ...);  
  4. int execle(const char *path, const char*arg, ..., char *const envp[]);  
  5. int execv(const char *path, char *constargv[]);  
  6. int execvp(const char *file, char *constargv[]);  
  7. int execve(const char *path, char *constargv[], char *const envp[]);  

最终调用的是execve,其他都是在这个函数的基础上进行封装的。

 

更改用户ID和组ID

[cpp]  view plain copy
  1. int setuid(uid_t uid)  
  2. int setgid(gid_t gid)  

如果进程具有超级用户权限,那么设置成功。

如果进程不具有超级用户权限,并且uid等于实际用户ID或者设置用户ID,那么仅仅能把有效用户ID设置为实际用户ID

如果上面都不满足就直接返回-1.

以上规则也适用于组ID


man命令切换的原理

man程序可能必须执行几个其它命令来处理包含要显示的手册页的文件。为了避免被欺骗以致运行错误的命令或覆写错误的文件,man命令必须在两个权限集里交换:运行man命令的用户和拥有man可执行文件的用户。下面的步骤会发生:


1、假设man程序文件被用户名man拥有并设置了设置用户ID,当我们exec它时,我们有:真实用户ID=我们的用户ID;有效用户ID=man;保存的设置用户ID=man。 

 

2、man程序访问所需的配置文件和手册页。这些文件被用户名man拥有,但是因为有效用户ID是man,所以文件访问被允许。 

 

3、在man为我们执行任何一个程序时,它调用setuid(getuid())。因为我们不是超级用户进程,所以这只改变有效用户ID。我们有:真实用户ID=我们的用户ID(不变);有效用户ID=我们的用户ID;保存的设置用户ID=man(不变)。现在man进程用我们的用户ID作为它的有效用户 ID运行。这意味着我们只能访问我们有普通权限的文件。我们没有更多的权限。它可以为我们执行任何过滤器。 

 

4、当过滤完成时,man调用setuid(euid),这里euid是用户名man的数值用户ID。(这是我们为什么需要保存的设置用户ID。)现在我们有:真实用户ID=我们的用户ID(不变);有效有用ID=man;保存的设置用户ID=man(不变)。 

 

5、man程序现在可以在它的文件上进行操作,因为它的用效用户ID是man。 

 

 

解释器文件、

system函数、

system调用fork execl waitpid这些函数其中fork、waitpid出错返回-1 execl出错返回127

正常返回是执行shell的终止状态

进程会计

调用accton + filename 文件名称

就会把进程执行完毕的一些信息(使用的CPU、用户ID和组ID、启动时间)记录到文件里面

下面这个程序就是解析这个文件的。程序是固定的

[cpp]  view plain copy
  1. /* 
  2.  *===================================================================================== 
  3.  * 
  4.  *      Filename:  accton.cpp 
  5.  * 
  6.  *   Description:  accton 
  7.  * 
  8.  *       Version:  1.0 
  9.  *       Created:  2012年05月04日 21时13分45秒 
  10.  *      Revision:  none 
  11.  *       Compiler:  gcc 
  12.  * 
  13.  *        Author:  LeoK, 
  14.  *  Organization:  
  15.  * 
  16.  *===================================================================================== 
  17.  */  
  18. #include <stdlib.h>  
  19. #include <stdlib.h>  
  20. #include <stdio.h>  
  21. #include <sys/types.h>  
  22. #include <sys/acct.h>  
  23. #include <unistd.h>  
  24. #include <errno.h>  
  25. //#include "errorout.h"  
  26. #define     err_sys  perror  
  27. #define     err_quit perror  
  28. #ifdef HAS_SA_STAT  
  29. #define FMT "%-*.*s e = %6ld, chars =%7ld, stat = %3u; %c %c %c\n"  
  30. #else  
  31. #define FMT "%-*.*s e = %6ld, chars =%7ld, %c %c %c\n"  
  32. #endif  
  33.    
  34. #ifndef HAS_ACORE  
  35. #define ARORE 0  
  36. #endif  
  37.    
  38. #ifndef HAS_AXSIG  
  39. #define AXSIG 0  
  40. #endif  
  41.    
  42. static unsigned long compt2ulong(comp_tcomptime)  
  43. {    
  44.    
  45.          unsignedlong   val;  
  46.          int             exp;  
  47.    
  48.          val= comptime & 0x1fff;                       //13-bit fraction  
  49.          exp= (comptime >> 13) & 7;                     //3 -bit exponent (0-7)  
  50.          while(exp-- >0)  
  51.                    val*=8;  
  52.          returnval;  
  53. }  
  54. int main(int argc,char *argv[]){  
  55.          structacct     acdata;  
  56.          FILE            *fp;  
  57.    
  58.          if(argc!= 2)  
  59.                    err_quit("usage:pracct filename");  
  60.          if((fp=  fopen(argv[1],"r")) == NULL)  
  61.                    err_sys("can'topen accton " );  
  62.          while(fread(&acdata,sizeof(acdata), 1,fp) ==1){  
  63.                    printf(FMT,(int)sizeof(acdata.ac_comm)  
  64.                                      ,(int)sizeof(acdata.ac_comm), acdata.ac_comm  
  65.                                      ,compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io)  
  66. #ifdef HAS_SA_STAT  
  67.                                      ,(unsignedchar) acdata,ac_stat  
  68. #endif  
  69.                                      ,acdata.ac_flag& ACORE ? 'D' : ' '  
  70.                                      ,acdata.ac_flag& AXSIG ? 'X' : ' '  
  71.                                      ,acdata.ac_flag& AFORK ? 'F' : ' '  
  72.                                      ,acdata.ac_flag& ASU   ? 'S' : ' '  
  73.                               );  
  74.    
  75.          }  
  76.          if(ferror(fp))  
  77.                    err_sys("readerror");  
  78.          exit(0);  
  79. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值