第8章进程控制
8.1 进程标识符
Pid: 0(调度进程,swapper)
Pid: 1(初始化进程,init),自举内核后启动UNIX系统,通常读与系统相关的初始化文件,并将系统引导至一个状态
Pid_t getpid(void); //获得当前进程id
Pid_t getppid(void);//获得父进程id
Uid_t getuid(void); //获得当前用户id
Uid_t geteuid(void);
Gid_t getgid(void);//获得当前组id
Git_t getgid(void);
8.2 fork()函数
Pid_t fork(void);//用于创建一个新进程(子进程),调用一次,返回两次)子进程的返回值为0,父进程的返回值为新子进程的进程ID
子进程是父进程的副本,资源相同但不共享
COW(Copy-On-Write,写时拷贝),当父进程或者子进程企图修改共享区域时,,内核只需修改区域的那块内存用于制作一个副本,即子进程的修改不会影响父进程,父子进程随机交叉执行
网络服务进程中常在fork()之后由父子进程关闭不需要使用的文件描述符(父子进程分别执行不同的程序段)
父进程等待子进程完成,无需对其描述符进行任何处理
父进程与子进程的相同属性,包括有效用户ID,有效用户组ID,实际用户ID,实际用户组ID,,设置用户ID,设置组ID,根目录,当前工作目录等等。
父进程与子进程的区别:pid,父进程ID等等P175
fork()的两种用法:
(1) 父进程等待服务请求到达,父进程调用fork()创建子进程处理此请求,父进程则继续等待下一个服务请求
(2) 一个进程要执行一个不同程序,子进程从fork()返回后立即调用exec();
8..3vfork()函数
确保子进程先运行,在调用exec或者exit之后父进程才可能调度运行;子进程在父进程的地址空间执行
8.4 exit函数
进程的5种正常终止方式
(1) 在main()函数内执行return语句
(2) 调用exit()函数,调用各种终止处理程序。关闭所有标准IO流
(3) 调用_exit或_Exit函数,无需运行终止处理程序或信号处理程序而终止的方法
(4) 进程的最后一个线程在启动例程执行返回语句
(5) 进程的最后一个线程调用pthread_exit()函数
无论程序如何终止,均会执行内核中的关闭所有打开描述符,释放所使用的存储器
当一个进程终止时,内核逐一检查所有活动进程,以判断是否是正要终止进程的子进程,人如果是则将该进程的父进程ID更改为1
如果子进程在父进程之前终止,当终止进程的父进程调用wait或者waitpid可以获取这些信息,内核便可以释放终止进程所有的存储区,关闭其打开的全部文件。
僵死进程:父进程未对子进程进行妥善的处理
8.6wait和waitpid函数
(1)如果所有的子进程均还在运行,则阻塞该进程
(2)如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态(3)如果并无子进程,则出错
Pid_t wait(int *statloc);
Pid_t waitpid(pid_t pid,int *staloc,intoptions);
Waitpid()与wait()相比的三大区别:
1)waitpid看可以根据pid传参数从而决定等待的特定子线程
2)waitpid提供了一个非阻塞的版本
3))waitpid支持作业控制
8,7waitid函数
Int waited(idtype_t idtype,id_tid,siginfo_t *infop,int options);
Idtype:P_PID(等待特定的子进程),P_PGID(等待特定进程组的任意子进程)P_ALL(任意的子进程)
Options:关注哪些状态改变
Infop:引起子进程状态改变的生成信号
8.8wait3和wait4
Pid_t wait3(int *statloc,int options,structrusage *rusage);
Pid_t wait4(pid_t pid,int *statloc,intoptions,struct rusage *rusage):
Resage:要求内核返回终止进程及其所有子进程的全部资源汇总
8.9 竞争条件
通过信号量控制父进程和子进程的执行顺序从而避免竞争WAIT_PARENT(),TELL_CHILD(父进程id),TELL_PARENT(getppid()),WAIT_CHILD()
8.10exec函数
Exec()实质上是执行了另外一段程序
8.11更改用户ID和组ID
最小特权模型:设计应用程序时,应当只具有为完成给定任务所需的最小特权
Int setuid(uid_t uid); //设置实际用户ID,有效用户ID
1) 如果进程具有超级用户特权,则setuid函数将实际用户ID,有效用户ID,设置用户ID设置为uid
2) 如果进程不具有超级用户特权,但uid、等于实际用户ID和设置用户ID,则将有效用户ID设置为uid
3) 只有超级用户进程可以更新实际用户ID
4) 仅对当前程序文件设置了设置用户ID时,exec()函数才会设置有效用户ID
5) 保存的设置用户ID是由exec()复制有效用户ID而得来的
Int setgid(gid_t gid); //设置实际组ID,有效组ID
8.12解释器文件、
!pathname [optional-argument] pathname(绝对路径)实际上执行的是pathname对应的文件
8.13 system函数
Int system(const char *cmdstring);//能进行所需的各种出错处理以及各种信号处理
8.14进程会计
类似于日志文件,使用accton命令启动会计处理,每当有进程终止时,会将该进程的命令名,CPU执行时间,用户ID,用户组ID,启动时间等等信息记录下来,
会计记录的结构是struct acct{
Char ac_flag; //进程完成的事件
Char ac_stat; //进程终止状态
Uid_t ac_uid; //进程的用户ID
Gid_t ac_gid; //进程的组ID
Dev_t ac_tty; //进程的控制终端
Time_t ac_btime; //启动日历时间
Comp_t ac_utime; //用户CPU时间
Comp_t ac_stime; //系统CPU时间
Comp_t ac_etime; //经过时间
Comp_t ac_men;//平均内存使用
Comp_t ac_io; //传输的字符数
Comp_t ac_rw; //读写的块数
Char ac_comm[8];//命令名
}
Fork()会新增会计记录
Execl()只会改变命令名
Abort()会产生信号SIGABRT,从而进行core转存
Kill()会产生SIGKILL信号,但只是终止该进程
8.15 用户标识
Char *getlogin(void);//获取登录名
8.16 进程时间
Clock_t times(struct tms *buf); //获取进程时间
Struct tms{
Clock_t tms_utime; //用户CPU时间
Clock_t tms_stime;//系统CPU时间
Clock_t tms_cutime;//终止子进程的用户CPU时间
Clock_t tms_cstime; //终止子进程的系统CPU时间
}