进程编程2 – Unix环境高级编程8章读书笔记

Process Control

1 Process Identifiers

1. PID=0一般是调度进程,又称为swapper

2. PID=1一般是init进程,在引导过程之后启动,负责启动整个UNIX系统,所有Orphaned子进程都会自动成为init的子进程

3. PID=2一般是页面守护进程,负责将虚拟内存的换页

4. 下面函数被用来获得pid

#include <unistd.h>

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 <unistd.h>

pid_t fork(void);

在子进程中返回0,父进程中返回实际的子进程pid-1则出错

1. 这个函数创建出一个和父进程相同的子进程,比较特别的是子进程也会从这个函数调用,和父进程只是返回值不同。

2. 子进程起初和父进程共享同样的物理内存,当某个页面被修改的时候,系统会给子进程分配新的空间给这个页面。这个行为被称为COW (Copy On Write)

3. 父进程还是子进程先继续执行无法预测

4. 父进程的所有文件描述符在子进程中都被调用dup函数复制,并且文件offset是在子进程和父进程之中共享的

5. 子进程继承了父进程的下列特性:

a. File Descriptors

b. Real user ID, real group ID, effective user ID, effective group ID

c. Supplementary group IDs

d. Process group ID

e. Session ID

f. Controlling terminal

g. Set-user-ID & set-group-ID flags

h. Working directory

i. Root directory

j. File mode creation mask

k. Signal mask & dispositions

l. The close-on-exec flag for any open file descriptions

m. Environment

n. Attached shared memory segments

o. Memory mapping

p. Resource limits

6. 子进程和父进程不同的地方有:

a. fork函数的返回值

b. Process ID

c. Parent process ID

d. tms_utime, tms_stime, tms_cutime, tms_cstime = 0

e. 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。处于这样状态的子进程被称为zombie

3. init进程inherit的子进程如果中止不会变成zombie,因为init会自动当进程结束的时候调用wait

5 wait & waitpid

1. 当子进程结束的时候,父进程会收到SIGCHLD通知

2. 进程可以调用wait / waitpid等待此Signal

a. 当所有子进程都在执行的时候,会block

b. 当某个子进程中止,成为zombie的时候,立刻返回

c. 如果没有任何子进程,则返回错误

3. waitwaitpid函数原型如下:

#include <unistd.h>

pid_t wait(void);

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

正常情况下返回pid,或者0waitpid在非block模式下才有可能返回),-1代表错误

4. 这两个函数区别如下:

a. Wait函数会因为等待子进程结束而block,而waitpid有一个option可以允许waitpid函数不block

b. waitpid等待某个特定进程

5. waitpid函数的statloc参数保存了退出进程的状态,当然也可以传NULL。这个状态通常和实现相关,不过可以用wait.h中定义的macro来检测。

a. WIFEXITED(status):返回值

b. WIFSIGNALED(status):返回造成退出的signalnumber

c. WIFSTOPPED(status):是否被stop,可以用WSTOPSIGstatus)来获得具体的signal

d. WIFCONTINUED(status):返回是否被continue

6. waitpidpid参数:

a. pid == -1:等待任意子进程,等价于wait

b. pid > 0,等待pid指定的子进程

c. pid == 0,等待和调用进程相同group id的任意子进程

d. pid < -1,等待任意group id = pid的绝对值的子进程

7. waitpidoptions参数可以是下面的组合:

a. WCONTINUED:等待任何指定的子进程在stop之后被continue

b. WNOHANG:如果还没有退出,不block,返回0

c. WUNTRACED:被stopped的进程

6 waitid

waitid函数在waitpid函数上提供了额外的灵活性:

#include <unistd.h>

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; /* SIGPOLLband数值 */

};

7 wait3 & wait4

这两个函数的用处是返回额外的关于进程资源使用状况的信息,以struct rusage *rusage的形式返回

8 exec functions

之前提到了vfork可以创建一个并非是父进程copy的子进程,专门用来调用exec函数。exec系列函数的作用是调用一个新的程序在该进程中运行,PID不改变,也没有创建新进程,只是replace当前的进程,该函数成功情况下不会返回。原型如下:

#include <unistd.h>

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,后面两个则是filenameFilename如果有’/’字符,则被认为是pathname,否则认为是filename并会在PATH路径指定的位置查找

2. execlpexecvp对于非可ELF执行文件会认为是Shell脚本来处理

3. execl, execlp, execle需要每个参数单独传递,NULL作为结束。而execv, execvp, execve则是传入argv

4. execle, execve可以传入环境,其他四个函数则会使用environ变量中的内容

总结一下,各个字符代表的意义是:

1. v:用argv传入参数

2. l:用每个参数作为argument传入

3. e:传入环境,没有的话则使用environ变量的值

4. p:传入的是filename,并会处理PATH的内容,否则是pathname

需要注意的是,exec调用之后进程的real user IDreal group ID不变,如果被执行的程序的set-user-ID位被设置,effective user ID被设置成owner ID。也就是说,如果一个normal userexec执行了一个ownersuperuser的程序,并且该程序的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 IDgroup 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 <unistd.h>

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-IDuid

b. 当进程没有超级用户权限,并且uid = real user ID / saved set-user-ID的话,setuid会修改effective user IDuid

c. 否则,errno被设置为EPERM,返回-1

d. 上面这些规则也适用于setgid

4. 对于uid下面规则成立:

a. 只有超级用户的进程才可以改变real user ID。一般情况下不会改变real user ID

b. 当程序的set-user-ID位被设置,exec才会设置effective user ID为程序的user ID,否则则不改变

c. Saved set-user-IDexec所设置的effective user ID拷贝而来

5. POSIX.1还提供了两个函数seteuidsetegid,用来修改effective user ID / effective group ID

a. 对于一般用户,只能设置effective user IDreal 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 IDreal group ID不变,如果被执行的程序的set-user-ID位被设置,effective user ID被设置成owner ID。也就是说,如果一个normal userexec执行了一个ownersuperuser的程序,并且该程序的set-user-id被设置,那么一旦该程序被运行,该程序的effective user id=super user (real user ID不变)这可能是潜在的安全问题。这个问题同样适用于system

11 User Identification

getlogin函数可以获得用户名:

#include <unistd.h>

char *getlogin(void);

成功返回具体name,错误返回NULL

12 Process Times

times函数可以获得和进程相关的时间:

#include <sys/times.h>

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
<stron

你可能感兴趣的:(编程,C++,c,unix,读书)