《Unix环境高级编程》读书笔记 第8章-进程控制,unix环境高级编程
《Unix环境高级编程》读书笔记 第8章-进程控制,unix环境高级编程
1. 进程标识
- 进程ID标识符是唯一、可复用的。大多数Unix系统实现延迟复用算法,使得赋予新建进程的ID不同于最近终止所使用的ID
- ID为0的进程通常是调度进程,也常被称为交换进程。它是内核的一部分,是系统进程。
- ID为1的进程通常是init进程,在自举过程结束时由内核调用。该进程负责在内核自举后启动一个Unix系统,它决不会终止,是一个普通的用户进程,但以超级用户特权运行。
- ID为2的进程是页守护进程,负责支持虚拟存储器系统的分页操作。
#include <unistd.h>
pid_t getpid(void);
Returns: process ID of calling process
pid_t getppid(void);
Returns: parent process ID of calling process
uid_t getuid(void);
Returns: real user ID of calling process
uid_t geteuid(void);
Returns: effective user ID of calling process
gid_t getgid(void);
Returns: real group ID of calling process
gid_t getegid(void);
Returns: effective group ID of calling process
- 注意:这些函数都没有出错返回。
2. 函数fork
- fork函数被调用一次,返回两次。子进程中返回值是0,父进程中返回值是子进程的pid
- 子进程是父进程的副本,子进程获得父进程的数据空间、堆和栈的副本。注意,在是子进程拥有的副本。父子进程并不共享这些存储空间部分。父子进程共享正文段。
- 由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、堆和栈的完全副本。作为替代,使用了写时复制技术。
- 4种平台都支持的变体:vfork;Linux的变体:clone系统调用,允许调用者控制哪些部分由父子进程共享。
- fork之后是父进程先执行还是子进程先执行是不确定的
- 父进程中的所有打开文件描述符都被复制到子进程中,父子进程为每个相同的打开描述符共享一个文件表项,故共享同一文件偏移量。如果父子进程写同一描述符执行的文件,又没有任何形式的同步,那么它们的输出就会混合。
- 在fork之后处理文件描述符有以下两种常见的情况:
strlen和sizeof的区别:前者不包括null字节,一次函数调用;后者包括null字节,编译时计算
除了文件描述符之外,父进程的很多其他属性也由子进程继承,包括:
父进程和子进程之间的区别具体如下:
fork失败的两个主要原因:
fork有以下两种用法:
3. 函数vfork
- vfork函数的调用序列和返回值与fork相同,但两者的语义不同:
4. 函数exit
- 5种
正常终止
方式: - 3种
异常终止
方式: - 不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应的进程关闭所有打开描述符,释放它所使用的存储器等。
注意:“退出状态”(3个exit函数的参数或main的返回值)区别于“终止状态”。在最后调用_exit时,内核将退出状态转换为终止状态。
如果父进程在子进程之前终止,则称子进程为
孤儿进程
。子进程 ppid变为1,称这些进程由init进程收养
。一个init进程收养的进程终止时,init会调用一个wait函数取得其终止状态,防止它成为僵尸进程。- 如果子进程在父进程之前终止,内核为每个终止子进程保存了一定量的信息,至少包括pid、该进程的终止状态以及该进程使用的CPU时间总量。内核可以释放终止进程所使用的所有存储区,关闭其所有打开文件。在Unix术语中,一个已经终止、但其父进程尚未对其进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程被称为
僵尸进程
zombie/defunct。
5. 函数wait、waitpid
- 当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。子进程终止是异步事件。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
Both return: process ID if OK, 0 (see later), or −1 on error
- 调用wait或waitpid的进程可能:
- 如果进程由于收到SIGCHLD信号而调用wait,我们期望wait会立即返回。
- wait与waitpid的区别
若statloc不是NULL,则终止进程的终止状态就存放在它所指向的单元内。该整型状态字由实现定义,其中某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生了core文件。
waitpid函数中的pid参数的解释:
pid == -1,等待任一子进程,等价于wait函数
waitpid函数中的options参数:WNOHANG(不阻塞)、WCONTINUED、WUNTRACED
如果一个进程fork一个子进程,但不要它等待子进程终止,也不希望子进程处于僵尸状态直到父进程终止,实现这一要求的诀窍是调用fork两次。
#include "apue.h"
#include <sys/wait.h>
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* first child */
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid > 0)
exit(0); /* parent from second fork == first child */
/*
* We’re the second child; our parent becomes init as soon
* as our real parent calls exit() in the statement above.
* Here’s where we’d continue executing, knowing that when
* we’re done, init will reap our status.
*/
sleep(2);
printf("second child, parent pid = %ld\n", (long)getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
err_sys("waitpid error");
/*
* We’re the parent (the original process); we continue executing,
* knowing that we’re not the parent of the second child.
*/
exit(0);
}
6. 函数waitid
#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
Returns: 0 if OK, −1 on error
- idtype参数:P_PID、P_PGID、P_ALL
- options参数:WEXITED、WNOHANG...
- infop参数是指向siginfo结构的指针
7. 函数wait3、wait4
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
Both return: process ID if OK, 0, or −1 on error
- 允许内核返回由终止进程及其所有子进程使用的资源概况,包括用户CPU时间总量、系统CPU时间总量、缺页次数、接收到信号的次数等。
man 2 getrusage
8. 竞争条件
- 当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则发生了竞争条件。fork函数是竞争条件活跃的滋生地。
#include "apue.h"
TELL_WAIT(); /* set things up for TELL_xxx & WAIT_xxx */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
/* child does whatever is necessary ... */
TELL_PARENT(getppid()); /* tell parent we’re done */
WAIT_PARENT(); /* and wait for parent */
/* and the child continues on its way ... */
exit(0);
}
/* parent does whatever is necessary ... */
TELL_CHILD(pid); /* tell child we’re done */
WAIT_CHILD(); /* and wait for child */
/* and the parent continues on its way ... */
exit(0);
- 5个例程
9. 函数exec
#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[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
All seven return: −1 on error, no return on success
- l表示列表list,新程序的每个命令行参数都是一个单独的参数,空指针结尾
- v表示矢量vector,指针数组
- e代表传递一个指向环境字符串指针数组的指针
p代表使用调用进程中的environ变量为新程序复制现有的环境
在执行exec后,pid没有改变。但新程序从调用进程继承了下列属性:
#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
Both return: 0 if OK, −1 on error
- 关于谁能更改ID的若干规则(这里讨论更改用户ID的规则,同样适用于组ID)
- 实际用户ID
ruid
、有效用户IDeuid
、保存的设置用户IDsSUID
。假定_POSIX_SAVED_IDS为真 - 关于内核所维护的3个用户ID,还有注意以下几点:
- 只有root进程可以更改ruid。通常,ruid是在用户登录时,由login程序设置的,而且决不会改变它。
- 仅当对程序文件设置了SUID位,exec函数才设置euid。没有设置SUID位,则euid = ruid。
- sSUID是由exec复制euid而得到的。
#include <unistd.h>
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
Both return: 0 if OK, −1 on error
- 规则:一个非root用户总能交换ruid和euid,这就允许一个设置了SUID的程序交换成普通用户权限后,可以再次交换会SUID权限
10.2 函数seteuid、setegid
#include <unistd.h>
int seteuid(uid_t uid);
int setegid(gid_t gid);
Both return: 0 if OK, −1 on error
- 对于非root用户,可将euid设置为其ruid或sSUID;这与setuid函数一样
对于root用户,可将其euid设置为uid,而ruid、sSUID保持不变
组ID:上面所说的一切都以类似方式适用于各个组ID。附属组ID不受setgid、setregid、setegid函数的影响。
11. 解释器文件
12. 函数system
#include <stdlib.h>
int system(const char *cmdstring);
Returns: (see below)
13. 进程会计
- accton命令启用会计处理;在Linux中,该文件是/var/account/pacct
typedef u_short comp_t; /* 3-bit base 8 exponent; 13-bit fraction */
struct acct
{
char ac_flag; /* flag (see Figure 8.26) */
char ac_stat; /* termination status (signal & core flag only) */
/* (Solaris only) */
uid_t ac_uid; /* real user ID */
gid_t ac_gid; /* real group ID */
dev_t ac_tty; /* controlling terminal */
time_t ac_btime; /* starting calendar time */
comp_t ac_utime; /* user CPU time */
comp_t ac_stime; /* system CPU time */
comp_t ac_etime; /* elapsed time */
comp_t ac_mem; /* average memory usage */
comp_t ac_io; /* bytes transferred (by read and write) */
/* "blocks" on BSD systems */
comp_t ac_rw; /* blocks read or written */
/* (not present on BSD systems) */
char ac_comm[8]; /* command name: [8] for Solaris, */
/* [10] for Mac OS X, [16] for FreeBSD, and */
/* [17] for Linux */
};
14. 用户标识
- 获取登陆名
#include <unistd.h>
char *getlogin(void);
Returns: pointer to string giving login name if OK, NULL on error
- 如果调用此函数的进程没有连接到用户登陆时所用的终端,则函数会失败。通常称这些进程为守护进程。
15. 进程调度
- 进程通过调整nice值选择以更低优先级运行。只有特权进程允许提高调度权限。
- nice值的范围在0~(2*NZERO)-1之间,NZERO为系统默认的nice值。nice值越小,优先级越高。
#include <unistd.h>
int nice(int incr);
Returns: new nice value − NZERO if OK, −1 on error
#include <sys/resource.h>
int getpriority(int which, id_t who);
Returns: nice value between −NZERO and NZERO−1 if OK, −1 on error
#include <sys/resource.h>
int setpriority(int which, id_t who, int value);
Returns: 0 if OK, −1 on error
16. 进程时间
#include <sys/times.h>
clock_t times(struct tms *buf );
Returns: elapsed wall clock time in clock ticks if OK, −1 on error
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 */
};
- 此结构没有包含墙上时钟时间。返回是是相对值,故需要end-start
- 由此函数返回的clock_t值都用_SC_CLK_TCK转换成秒
原创文章,转载请声明出处:http://www.cnblogs.com/DayByDay/p/3948380.html
今天,我读到了《父亲的病》,《父亲的病》是鲁迅用文章去责骂那些庸医,就是他们把鲁迅的父亲治死了,让鲁迅非常气愤。
《父亲的病》是讲了鲁迅的爸爸被几位庸医治死,第一次治的时候,是叶天士医生来诊断病情,吃了他开的药后却没有见效。第二次是被本领高的陈莲河医生所治,用药也不同了,最后还是没治好。最后是著名的西医凡国手,他用的药最终以失败告终。最后,鲁迅的父亲死了。
读完后我心想:他们这些医生,光想着赚钱,这么多人也治不好一个病人。在鲁迅先生的资料里曾经介绍鲁迅先生早年留学日本学医, 我又猜测,鲁迅先生学医是不是和他父亲的死有关系呢?当我把这想法告诉妈妈时,妈妈点头说:“很有可能。他父亲的死对他打击很大,这也许是他学医的原因之一吧。”
评论暂时关闭