进程标识符
每个进程都有一个非负整型表示的唯一进程ID。进程ID可以重用。
ID为0的进程通常是调度进程,常常被称为交换进程。ID为1的进程通常是init进程,在自举过程结束时由内核调用。ID为2是页守护进程,负责支持虚拟存储系统的分页操作。
除了进程ID,每个进程还有一些其他的标识符。下列函数返回这些标识符。
#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
fork函数
一个现有进程可以调用fork函数创建一个新进程。
pid_t fork(void);
由fork创建的新进程被称为子进程。fork函数被调用一次,但返回两次。子进程的返回值是0,父进程的返回值是子进程的进程ID。
子进程是父进程的副本。子进程获得父进程数据空间、堆和栈副本。父、子进程共享正文段。
vfork函数
vfork用于创建一个新进程,而该新进程的目的是exec一个新程序。
vfork并不将父进程的地址空间完全复制到子进程中,在子进程调用exec或exit之前,它在父进程的空间中运行。
vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。
对于父进程已经终止的所有进程,它们的父进程都改变为init进程。我们称这些进程由init进程领养。其操作过程大致如下:
在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的子进程,如果是,则将该进程的父进程ID更改为1。内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息。一个已经终止、但是其父进程尚未对其进行善后处理的进程被称为僵死进程。
wait和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。父进程可以选择忽略该异步信号,或者提供一个该信号发生时即被调用执行的函数。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
如果调用者有多个子进程,只要其中一个子进程终止时,wait就立即返回。如果调用者需要等待某个子进程返回,可以使用waitpid函数。
对于waitpid函数中pid参数的作用解释如下:
pid == -1 等待任一子进程。
pid > 0 等待其进程ID为pid的子进程。
pid == 0 等待其组ID等于调用进程组ID的任一子进程。
pid < -1 等待其组ID等于pid绝对值的任一子进程。
exec函数
当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ...);
int execlp(const char *filename, const char *arg0, ...);
int execle(const char *pathname, const char *arg0, ..., char * const envp[]);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *filename, char *const argv[]);
int execve(const char *pathname, char *const argv[], char *const envp[]);
exec函数中l表示list,v表示vector,l和v参数需要以NULL结尾,如果filename中包含/,则将其视为路径名。