Apue.2e Chapter8 Process Control

8~11章主要讲进程相关的内容。

进程标示符(PID),0是调度进程,1通常是init进程,2一般是页守护进程(pagedaemon),负责虚拟内存的分页操作。

#include <unistd.h>

pid_t getpid(void);

pid_t getppid(void);    //parent pid

uid_t getuid(void);

uid_t geteuid(void);

gid_t getgid(void);

gid_t getegid(void);

区分实际ID,有效ID,保存的ID。

fork函数

pid_t fork();

子进程是父进程的副本,获得父进程空间的数据空间、stack、heap的副本,共享.text段。写时复制。

Linux:clone,fork的泛型,可以控制父子进程共享的部分;

父进程所有打开的文件描述符会被复制到子进程中,相同的描述符之间共享同一个文件表项;

父子进程之间有大量属性被继承,除了:进程ID、文件锁、闹钟、信号集。

fork后如果立刻执行新程序,也可以调用spawn;

废弃接口:vfork,建立子进程并阻塞父进程知道exit或exec被调用,父子进程之间共享数据段(子进程在父进程地址空间运行);

exit函数

_Exit(ISO)和_exit(Posix.1)在UNIX下同义,exit会执行全局的flush。

退出状态和终止状态:后者指的是进程结束状态,前者指的是传递给exit函数的参数或main的返回值;

如果父进程在子进程结束前终止,子进程将被init进程认领,其父进程自动转为init进程;

如果一个子进程已经结束,其父进程未做好善后处理,这样的进程就是所谓的僵尸进程(zombie,用ps显示为z);

wait和waitpid

#include <sys/wait.h>

pid_t wait(int *statloc);

pid_t waitpid(pid_t pid, int *statloc, int options);

wait的功能简单:等待任意子进程,阻塞父进程,等待到后返回子进程pid;

waitpid可以指定等待进程的pid(pid>0),或任意子进程(pid=-1),或指定组id(pid<-1),或要求组ID=调用进程组id;

options有些选项可以扩展功能,包括作业支持和非阻塞等待(WNOHANG,返回0)。

statloc如果非0,可以存放子进程终止状态,然后使用四个互斥的宏来解析这些状态,包括:

WIFEXITED:正常终止,可以通过WEXITSTATUS来获得终止返回值;

WIFSIGNALED:异常终止,可执行WTERMSIG取使之终止的信号编号;有些实现可以用WCOREDUMP取得核心转储文件;

WIFSTOPPED:若为当前暂停子进程返回的状态,则为真;可使用WSTOPSIG取使子进程暂停的信号编号;

WIFCONTINUED:若在作业控制暂停后已经继续的子进程返回了状态,则为真。

waitid //SUS

#include <sys/wait.h>

int waited(idtype_t idtype,id_t id, siginfo_t *infop, int options);

idtype可取:

P_PID:等待一个特定的进程;

P_PGID:等待一个特定进程组中的任一子进程;id包含要等待子进程的进程组ID;

P_ALL:等待任一子进程,忽略id;

options的参数与waitpid类似,包括作业控制、非阻塞等待等功能;

infop参数时指向siginfo的指针,该结构包含了有关引起子进程状态改变的生成信号的详细信息。

wait3和wait4

非posix,多一个参数,返回由终止进程及其所有子进程使用的资源汇总。

竞争条件

当多个进程以不确定的顺序处理共享数据,这就发生了竞争条件。避免的方法是使用轮询(耗时)或使用信号进行异步编程。

这里的例程用到了后面signal部分的知识。

exec函数

共6个exec系的函数,包括execl, execlp, execle, execv, execvp, execve,其中l表示list,p表示path,v表示vector,e表示environment,以p结尾的函数第一个参量都是filename,如果实参中不包括/,那么系统会在PATH中进行搜索该文件名;l和v是两种不同传递字符串参数的方法;有e的还有再传递环境变量(否则会继承父进程的);

参数表的长度有一个限制,如果超出长度,可以使用xargs来拆分参数;

exec系不会改变子进程的ID和一系列属性,对于文件的处理和FD_CLOEXEC标志有关,目录流一定会被关闭;在很多UNIX实现中,这6个函数只有execve是system call,其余都是library function

更改用户ID和组ID

#include <unistd.h>

int setuid(uid_t uid);

int setgid(gid_t gid);

su:real id,enable id和saved id都改为uid;

非su,uid=real id或saved id,此时设置enable id=uid;

非su,且不满足上述条件,errno=EPERM,返回-1;saved id需要_POSIX_SAVED_IDS为真;

如果没有setuid,exec系不会改变文件的enable id;saved id是exec函数复制enable id而来;

int setreuid(uid_t ruid, uid_t euid);

int setregid(gid_t rgid, gid_t egid);

交换real id和enable id。

int seteuid(uid_t uid);

int setegid(gid_t gid);

更改enable id。

解释器文件

就是某些脚本文件,可以指定解释。

第一行的形式必须是#!pathname [optional-argument]

pathname指定解释器[绝对]路径,后面跟的是可选参数。一般有长度限制

解释器文件一般用来写其他(除了/bin/sh)之外的shell脚本文件。

system函数

标准库函数。如果实参传递NULL,则返回非0表示system函数可用;

在Unix中,system本质是fork+exec+waitpid;

如果fork失败,或waitpid返回除EINTER之外的出错,返回-1,errno改变;

如果exec失败,返回值同shell执行 exit(127);

否则所有3个函数都执行成功,返回shell的终止状态(即waitpid中的statloc);

system函数的优势:集成了各种错误处理和信号机制;

注意setuid或者setgid的程序绝不应该调用system函数,否则会引起安全方面的漏洞。

进程会计

某个Unix的选项功能,激活后可以让内核处理记录进程的某些统计数据,可以将之重定向到文件,进行日志记录。

用户标识

获得用户登录名:

char *getlogin(void);

然后就可以通过该函数的返回值调用getpwnam等函数取得其他所需信息。

进程时间

#include <sys/times.h>

clock_t times(struct tms *buf);

填充tms结构的元素包括用户cpu时间、系统cpu时间和已结束的子进程相关时间;

返回墙上时钟时间。

原文地址:https://www.cnblogs.com/livewithnorest/p/2878911.html