7.1 进程标识
每个进程都有一个非负整型的唯一进程ID。因为进程ID标识符总是唯一的,常将其用做其他标识符的一部分以保证其唯一性。
有某些专用的进程:进程ID 0是调度进程,常常被称为交换进程(swapper)。该进程并不执行任何磁盘上的程序—它是内核的一部分,因此也被称为系统进程。进程ID 1通常是init进程,在自举过程结束时由内核调用。
在某些UNIX的虚存实现中,进程ID 2是页精灵进程(pagedaemon)。此进程负责支持虚存系 统的请页操作。与交换进程一样,页精灵进程也是内核进程。
除了进程ID,每个进程还有一些其他标识符。下列函数返回这些标识符。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); //返回:调用进程的进程ID
pid_t getppid(void); //返回:调用进程的父进程ID
uid_t getuid(void); //返回:调用进程的实际用户ID
uid_t geteuid(void); //返回:调用进程的有效用户ID
gid_t getgid(void); //返回:调用进程的实际组ID
gid_t getegid(void); //返回:调用进程的有效组ID
注意,这些函数都没有出错返回。
7.2 fork函数
一个现存进程调用fork函数是UNIX内核创建一个新进程的唯一方法 (这并不适用于前节提及的交换进程、init进程和页精灵进程。这些进程是由内核作为自举过程的一部分以特殊方式创建的)。
#include <sys/types.h>
#include <unistd.h>
//返回:子进程中为0,父进程中为子进程ID,出错为-1
pid_t fork(void);
由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是 0,而父进程的返回值则是新子进程的进程 ID。
子进程和父进程继续执行 fork之后的指令。子进程是父进程的复制品。例如,子进程获得父进程数据空间、堆和栈的复制品。注意,这是子进程所拥有的拷贝。父、子进程并不共享这些存储空间部分。如果正文段是只读的,则父、子进程共享正文段。
7.3 vfork函数
vfork与fork一样都创建一个子进程, 但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec (或exit ),于是也就不会存访该地址空间。
vfork保证子进程先运行,在它调用exec或exit之后父进 程才可能被调度运行。
7.4 wait和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生 ),所以这种信号也是内核向父进程发的异步通知。父进程可以忽略该信号,或者提供一个该信号发生时即被调用执行的函数 (信号处理程序)。对于这种信号的系统默认动作是忽略它。调用wait或waitpid的进程可能会:
- 阻塞(如果其所有子进程都还在运行)。
- 带子进程的终止状态立即返回(如果一个子进程已终止,正等待父进程存取其终止状态 )。
- 出错立即返回(如果它没有任何子进程)。
如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回。但是如果在一 个任一时刻调用wait,则进程可能会阻塞。
#include <sys/types.h>
#include <sys/wait.h>
//两个函数返回:若成功则为进程 I D,若出错则为-1
pid_t wait(int *statloc) ;
pid_t waitpid(pid_t pid, int *statloc, int options) ;
这两个函数的区别是:
- 在一个子进程终止前, wait 使其调用者阻塞,而 waitpid 有一选择项,可使调用者不阻 塞。
- waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的进程。
如果一个子进程已经终止,是一个僵死进程,则 wait立即返回并取得该子进程的状态,否 则wait使其调用者阻塞直到一个子进程终止。如调用者阻塞而且它有多个子进程,则在其一个 子进程终止时,wait就立即返回。因为wait返回终止子进程的进程ID,所以它总能了解是哪一个子进程终止了。
这两个函数的参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数指定为空指针。
宏 | 说明 |
WIFEXITED(status) | 若为正常终止子进程返回的状态,则为真。对于这种情况可执行 WEXITSTATUS(status) 取子进程传送给exit或_exit参数的低8位 |
WIFSIGNALED(status) | 若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行 WTERMSIG(status) 取使子进程终止的信号编号。 另外, WCOREDUMP(status) 若已产生终止进程的core文件,则它返回真 |
WIFSTOPPED(status) | 若为当前暂停子进程的返回的状态,则为真。对于这种情况,可执行 WSTOPSIG(status) 取使子进程暂停的信号编号 |
7.5 exec函数
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其 main函数开始执行。 因为调用exec并不创建新进程,所以前后的进程 ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。
有六种不同的exec函数可供使用,它们常常被统称为exec函数。
#include <unistd.h>
//六个函数返回:若出错则为- 1,若成功则不返回
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[] );
7.6 system函数
#include <stdlib.h>
int system(const char *cmdstring) ;
如果cmdstring是一个空指针,则仅当命令处理程序可用时, system返回非0值,这一特征可以决定在一个给定的操作系统上是否支持system函数。
因为system在其实现中调用了fork、exec和waitpid,因此有三种返回值:
(1)如果fork失败或者waitpid返回除EINTR之外的出错,则system返回-1,而且errno中设置了错误类型。
(2)如果exec失败(表示不能执行shell ),则其返回值如同shell执行了exit(127)一样。
(3)否则所有三个函数( fork , exec和waitpid)都成功,并且system的返回值是shell的终止状态
7.7 进程时间
墙上时钟时间、用户CPU时间和系统CPU时间。任一进程都可调用times函数以获得它自己及终止子进程的上述值。
#include <sys/times.h>
//返回:若成功则为经过的墙上时钟时间(单位:滴答),若出错则为-1
clock_t times(struct tms)* buf) ;
此函数填写由buf指向的tms结构,该结构定义如下:
struct tms
{
clock_t tms_utime; //user cpu time
clock_t tms_stime; //system cpu time
clock_t tms_cutime; //user cpu time, terminate children
clock_t tms_cstime; //system cpu time, terminate children
};