Process Control1 Process Identifiers
1. PID=0一般是调度进程,又称为swapper2. PID=1一般是init进程,在引导过程之后启动,负责启动整个UNIX系统,所有Orphaned子进程都会自动成为init的子进程3. PID=2一般是页面守护进程,负责将虚拟内存的换页4. 下面函数被用来获得pid:
#include pid_t getpid(void); 返回进程pid pid_t getppid(void); 返回父进程的pid uid_t getuid(void); 返回进程的user id uid_t geteuid(void); 返回进程的effective user id gid_t getgid(void); 返回进程的group id gid_t getegid(void); 返回进程的effective group id |
2 fork
fork函数原型如下:
#include pid_t fork(void); 在子进程中返回0,父进程中返回实际的子进程pid,-1则出错 |
1. 这个函数创建出一个和父进程相同的子进程,比较特别的是子进程也会从这个函数调用,和父进程只是返回值不同。2. 子进程起初和父进程共享同样的物理内存,当某个页面被修改的时候,系统会给子进程分配新的空间给这个页面。这个行为被称为COW (Copy On Write)3. 父进程还是子进程先继续执行无法预测4. 父进程的所有文件描述符在子进程中都被调用dup函数复制,并且文件offset是在子进程和父进程之中共享的5. 子进程继承了父进程的下列特性:a. File Descriptorsb. Real user ID, real group ID, effective user ID, effective group IDc. Supplementary group IDsd. Process group IDe. Session IDf. Controlling terminalg. Set-user-ID & set-group-ID flagsh. Working directoryi. Root directoryj. File mode creation maskk. Signal mask & dispositionsl. The close-on-exec flag for any open file descriptionsm. Environmentn. Attached shared memory segmentso. Memory mappingp. Resource limits6. 子进程和父进程不同的地方有:a. fork函数的返回值b. Process IDc. Parent process IDd. tms_utime, tms_stime, tms_cutime, tms_cstime = 0e. File lock不被子进程继承f. Pending alarm被清除g. Pending signal被清除7. fork失败的可能性有:a. 系统中的进程过多b. 超过系统设置的limit
3 vfork
vfork的原型和返回值和fork相同,区别在于:1. 创建的子进程和父进程共享同一个地址空间2. 创建的子进程一般的作用是再调用exec创建一个新的进程,在某些系统上可能会有一些优化
4 exit
1. 当父进程先于子进程退出的时候,子进程会变成orphaned并被init进程“收养”,init成为这些子进程的新的父进程。2. 当子进程先于父进程退出的时候,而且父进程也没有用wait, waitpid函数等待子进程结束,则子进程的部分信息会被保存起来,如PID,退出状态,CPU时间等,直到子进程被wait。处于这样状态的子进程被称为
zombie3. 被init进程inherit的子进程如果中止不会变成zombie,因为init会自动当进程结束的时候调用wait
5 wait & waitpid
1. 当子进程结束的时候,父进程会收到SIGCHLD通知2. 进程可以调用wait / waitpid等待此Signal:a. 当所有子进程都在执行的时候,会blockb. 当某个子进程中止,成为zombie的时候,立刻返回c. 如果没有任何子进程,则返回错误3. wait和waitpid函数原型如下:
#include pid_t wait(void); pid_t waitpid(pid_t pid, int *statloc, int options); 正常情况下返回pid,或者0(waitpid在非block模式下才有可能返回),-1代表错误 |
4. 这两个函数区别如下:a. Wait函数会因为等待子进程结束而block,而waitpid有一个option可以允许waitpid函数不blockb. waitpid等待某个特定进程5. waitpid函数的statloc参数保存了退出进程的状态,当然也可以传NULL。这个状态通常和实现相关,不过可以用wait.h中定义的macro来检测。a. WIFEXITED(status):返回值b. WIFSIGNALED(status):返回造成退出的signal的numberc. WIFSTOPPED(status):是否被stop,可以用WSTOPSIG(status)来获得具体的signald. WIFCONTINUED(status):返回是否被continue6. waitpid的pid参数:a. pid == -1:等待任意子进程,等价于waitb. pid > 0,等待pid指定的子进程c. pid == 0,等待和调用进程相同group id的任意子进程d. pid < -1,等待任意group id = pid的绝对值的子进程7. waitpid的options参数可以是下面的组合:a. WCONTINUED:等待任何指定的子进程在stop之后被continueb. WNOHANG:如果还没有退出,不block,返回0c. WUNTRACED:被stopped的进程
6 waitid
waitid函数在waitpid函数上提供了额外的灵活性:
#include int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); 成功返回0,错误返回-1 |
1. idtype_t可以是下面的值之一:a. P_PID:等待某指定的子进程b. P_PGID:等待任意指定group id的子进程c. P_ALL:任意子进程,id会被忽略2. Options可以是下面的值:a. WCONTINUED:等待被stop并且被continue的进程b. WEXITED:等待退出的进程c. WNOHANG:如果没有子进程需要等待则立刻返回d. WNOWAIT:不会将zombie子进程的退出状态撤销,下次调用wait系列函数的时候还可以继续获得这个退出状态e. WSTOPPED:等待被stop的进程3. Infop指向一个siginfo_t结构,表示使得进程退出的signal的产生原因:
struct siginfo_t { int si_signo; /* signal */ int si_errno; /* errrno */ int si_code; /* 额外信息,和具体signal相关 */ pid_t si_pid; /* 发送signal进程的pid */ uid_t si_uid; /* 发送signal的进程的uid */ void *si_addr; /* 产生fault(SIG_SEGV)的地址 */ int si_status; /* 退出值或者signal数值 */ long si_band; /* SIGPOLL的band数值 */ }; |
7 wait3 & wait4
这两个函数的用处是返回额外的关于进程资源使用状况的信息,以struct rusage *rusage的形式返回
8 exec functions
之前提到了vfork可以创建一个并非是父进程copy的子进程,专门用来调用exec函数。exec系列函数的作用是调用一个新的程序在该进程中运行,PID不改变,也没有创建新进程,只是replace当前的进程,该函数成功情况下不会返回。原型如下:
#include int execl(const char *pathname, const char *arg0, … /* (char *) 0 */ ); int execv(const char *pathname, char *const argv[]); int execle(const char *pathname, const char *arg0, … /* (char *)0, char *const envp[] */); int execve(const char *pathname, char *const argv[], char *const envp[]); int execlp(const char *filename, const char *arg0, … /* (char *)0 */ ); int execvp(const char *filename, char *const argv[]); 错误返回-1,成功不会返回 |
1. 前面4个是pathname,后面两个则是filename。Filename如果有’/’字符,则被认为是pathname,否则认为是filename并会在PATH路径指定的位置查找2. execlp和execvp对于非可ELF执行文件会认为是Shell脚本来处理3. execl, execlp, execle需要每个参数单独传递,NULL作为结束。而execv, execvp, execve则是传入argv4. execle, execve可以传入环境,其他四个函数则会使用environ变量中的内容总结一下,各个字符代表的意义是:1. v:用argv传入参数2. l:用每个参数作为argument传入3. e:传入环境,没有的话则使用environ变量的值4. p:传入的是filename,并会处理PATH的内容,否则是pathname需要注意的是,exec调用之后进程的real user ID和real group ID不变,如果被执行的程序的set-user-ID位被设置,effective user ID被设置成owner ID。也就是说,如果一个normal user用exec执行了一个owner为superuser的程序,并且该程序的set-user-id被设置,那么一旦该程序被运行,该程序的effective user id=super user,这可能是潜在的安全问题。
9 Change User IDs and Group IDs
1. User ID & Group ID的意义如下:
ID | Description |
real user ID real group ID | 真正的user ID和group ID |
effective user ID effective group ID supplementary group ID | 用于文件访问权限检查 |
saved set-user-ID saved set-group-ID | 由exec保存起来。如果没有提供这个feature(通过_POSIX_SAVED_IDS)可以判断,则这些ID不存在 |
2. setuid 和setgid函数用来设置User ID / Group ID,原型如下:
#include int setuid(uid_t uid); int setgid(gid_t gid); 成功返回0,错误返回-1 |
3. setuid的规则如下:a. 有超级用户的权限的进程,setuid会修改real user id, effective user ID, & saved set-user-ID为uidb. 当进程没有超级用户权限,并且uid = real user ID / saved set-user-ID的话,setuid会修改effective user ID为uidc. 否则,errno被设置为EPERM,返回-1d. 上面这些规则也适用于setgid4. 对于uid下面规则成立:a. 只有超级用户的进程才可以改变real user ID。一般情况下不会改变real user IDb. 当程序的set-user-ID位被设置,exec才会设置effective user ID为程序的user ID,否则则不改变c. Saved set-user-ID从exec所设置的effective user ID拷贝而来5. POSIX.1还提供了两个函数seteuid和setegid,用来修改effective user ID / effective group IDa. 对于一般用户,只能设置effective user ID为real user ID或者saved set-user-ID,不能为其他值b. 对于超级用户,可以设置effective user ID为任意值
10 system
1. System函数用于执行参数中给出的string,调用程序2. System函数的基本实现是调用fork,在子进程中调用exec执行/bin/sh或者其他shell,执行命令,父进程调用waitpid等待子进程的结束3. 之前在讨论exec的时候提到过, exec调用之后进程的real user ID和real group ID不变,如果被执行的程序的set-user-ID位被设置,effective user ID被设置成owner ID。也就是说,如果一个normal user用exec执行了一个owner为superuser的程序,并且该程序的set-user-id被设置,那么一旦该程序被运行,该程序的effective user id=super user (real user ID不变),这可能是潜在的安全问题。这个问题同样适用于system
11 User Identification
getlogin函数可以获得用户名:
#include char *getlogin(void); 成功返回具体name,错误返回NULL |
12 Process Times
times函数可以获得和进程相关的时间:
#include clock_t times(struct tms *buf); 成功返回wall clock time,错误返回-1 |
tms结构如下:
struct tms { clock_t tms_utime; /* user CPU time */ clock_t tms_stime; /* system CPU time */ clock_t tms_cutime; /* user CPU time, terminated children */ clock_t tms_cstime; /* system CPU time, terminated children */ }; |
作者: ATField
Blog:
http://blog.csdn.net/atfield