第八章 进程控制
引言:
如何创建进程?执行程序?进程终止?
讲述进程属性的各种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可能的情况
如果子进程正在运行,则阻塞
如果没有子进程就直接出错返回
如果子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态时立即返回。
- pid_t wait(int &status)
- pid_t waitpid(pid_t pid, int *status, intoptions)
如果pid == 0 等待其组ID等于调用进程组ID的任意子进程
pid== -1 等待任意子进程
pid》0 等待pid位pid的子进程
pid《0 等待pid为|pid|的子进程。
exec*函数
- #include <unistd.h>
- int execl(const char *path, const char*arg, ...);
- int execlp(const char *file, const char*arg, ...);
- int execle(const char *path, const char*arg, ..., char *const envp[]);
- int execv(const char *path, char *constargv[]);
- int execvp(const char *file, char *constargv[]);
- int execve(const char *path, char *constargv[], char *const envp[]);
最终调用的是execve,其他都是在这个函数的基础上进行封装的。
更改用户ID和组ID
- int setuid(uid_t uid)
- 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、启动时间)记录到文件里面
下面这个程序就是解析这个文件的。程序是固定的
- /*
- *=====================================================================================
- *
- * Filename: accton.cpp
- *
- * Description: accton
- *
- * Version: 1.0
- * Created: 2012年05月04日 21时13分45秒
- * Revision: none
- * Compiler: gcc
- *
- * Author: LeoK,
- * Organization:
- *
- *=====================================================================================
- */
- #include <stdlib.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/acct.h>
- #include <unistd.h>
- #include <errno.h>
- //#include "errorout.h"
- #define err_sys perror
- #define err_quit perror
- #ifdef HAS_SA_STAT
- #define FMT "%-*.*s e = %6ld, chars =%7ld, stat = %3u; %c %c %c\n"
- #else
- #define FMT "%-*.*s e = %6ld, chars =%7ld, %c %c %c\n"
- #endif
- #ifndef HAS_ACORE
- #define ARORE 0
- #endif
- #ifndef HAS_AXSIG
- #define AXSIG 0
- #endif
- static unsigned long compt2ulong(comp_tcomptime)
- {
- unsignedlong val;
- int exp;
- val= comptime & 0x1fff; //13-bit fraction
- exp= (comptime >> 13) & 7; //3 -bit exponent (0-7)
- while(exp-- >0)
- val*=8;
- returnval;
- }
- int main(int argc,char *argv[]){
- structacct acdata;
- FILE *fp;
- if(argc!= 2)
- err_quit("usage:pracct filename");
- if((fp= fopen(argv[1],"r")) == NULL)
- err_sys("can'topen accton " );
- while(fread(&acdata,sizeof(acdata), 1,fp) ==1){
- printf(FMT,(int)sizeof(acdata.ac_comm)
- ,(int)sizeof(acdata.ac_comm), acdata.ac_comm
- ,compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io)
- #ifdef HAS_SA_STAT
- ,(unsignedchar) acdata,ac_stat
- #endif
- ,acdata.ac_flag& ACORE ? 'D' : ' '
- ,acdata.ac_flag& AXSIG ? 'X' : ' '
- ,acdata.ac_flag& AFORK ? 'F' : ' '
- ,acdata.ac_flag& ASU ? 'S' : ' '
- );
- }
- if(ferror(fp))
- err_sys("readerror");
- exit(0);
- }