每一个进程都有一个非负整型表示的唯一进程ID。因为进程ID标识符总是唯一的,常将其用作其他标识符的一部分以保证其唯一性。虽然是唯一的,但是进程ID可以重用。大多数UNIX系统实现延迟重用算法,使得赋予新建进程的ID不同于最近终止进程所使用的ID。这防止了将新进程误认为是使用同一ID的某个已终止的先前进程。
系统中有一些专用的进程,但具体细节因实现而异。ID为0的进程通常是调度进程,常常被称为交换进程(swapper)。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。进程ID 1通常是init进程,在自举过程结束时由内核调用。
除了进程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函数创建一个新进程。
#include <unistd.h>
pid_t fork(void);
由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值为0,而父进程的返回值为新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值为0的理由:一个进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID(进程ID 0总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。
子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段。
由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全复制。作为替代,使用了写时复制技术。这些区域由父、子进程共享,而且内核将它们的访问权限改变为只读的。一般来说,fork之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。
fork有下面两种用法:
1.一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求的到达。
2.一个进程要执行一个不同的程序,这对Shell是常见的情况。这种情况下,子进程从fork返回后立即调用exec。
vfork函数
vfork函数的调用序列和返回值与fork相同,但两者的语义不同。vfork用于创建一个新进程。而该新进程的目的是exec一个新程序。vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec,于是也就不会访问该地址空间。vfork和fork之间的另一个区别是:vfork保证子进程先执行,在它调用exec或exit之后父进程才可能被调度运行。
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。子进程终止是个异步事件,所以这种信号也是内核向父进程发的异步通知。调用wait或waitpid的进程可能发生的情况:
1.如果其所有子进程都还在运行,则堵塞。
2.如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
3.如果它没有任何一个子进程,则立即出错返回。
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);
两个函数的返回值:若成功则返回进程的ID ,若出错则返回-1。
这两个函数的区别如下:
1.在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
2.waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
这两个函数的参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。依据传统,这两个函数返回的整型状态字是有现实定义的。其中,某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生一个core文件等。POSIX.1规定终止状态用定义在<sys/wait.h>中的各个宏查看。有四个互斥的宏可用来取得进程终止的原因,它们的名字都以WIF开始。
wait和waitpid返回的终止状态的宏
WIFEXITED(status),若为正常终止子进程返回的状态,则为真。对于这种情况可执行WEXITSTATUS(status),取子进程传送给exit、_exit和_Exit参数的低8位。
WIFSIGNALED(status),若为异常终止子进程返回的状态,则为真(接收到一个不捕捉的信号)。对于这种情况,可执行WTERMSIG(status),取使子进程终止的信号编号。
WIFSTOPED(status),若为当前暂停子进程的返回状态,则为真。对于这种情况,可执行WSTOPSIG(status),取使子进程暂停的信号编号。
WIFCONTINUED(status),若在作业控制暂停后已经继续的子进程返回的状态,则为真。
如果一个进程有几个子进程,那么只要有一个子进程终止,wait就返回。如果要等待一个指定的进程终止,则使用waitpid。对于waitpid函数中pid参数的作用:
pid==-1;等待任一子进程。就这一方面而言,waitpid和wait等效。
pid>0;等待其进程ID 与pid相等的子进程
pid==0;等待其组ID 等于调用进程组ID的任一子进程。
pid<-1;等待其组ID等于pid绝对值的任一子进程。