1:进程ID
每个进程都有一个非负整数表示的唯一进程ID。虽然是唯一的,但是进程ID可以重用。大多数Unix系统实现延迟重用算法。进程ID用pid_t表示。
一下两个函数用来得到当前进程和父进程的pid。
#include<unistd.h>
pid_t getpid();//get pid of current processs
pid_t getppid();//get pid of parent process
2:创建新进程
#include<unistd.h>
pid_t fork();
fork函数被调用一次,但返回两次。子进程返回0,父进程返回子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数可以得到一个进程的所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只有一个父进程,并且子进程可以通过getppid得到父进程的进程ID。
#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<sys/wait.h>
int glob=6;
char buf[]="a write to stdout\n";
void printErr(const char* info){
write(STDERR_FILENO,info,strlen(info));
_exit(1);
}
int main(){
int var=10;
pid_t pid;
if(write(STDOUT_FILENO,buf,strlen(buf))!=strlen(buf))
printErr("Write Error\n");
printf("pid=%d,before fork1\n",getpid());
if((pid=fork())<0)//括号不能少
printErr("fork error\n");
else if(pid==0){
glob++;
var++;
}else{
sleep(5);
wait(NULL);//在wait返回前子进程是僵尸进程,返回后就不是僵尸进程了
printf("father after waitpid\n");
sleep(5);
}
printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);
return 0;
}
编译后可执行文件为test1,
在终端执行 ./test1,输出如下:
a write to stdout
pid=5272,before fork1
pid=5273,glob=7,var=11
father after waitpid
pid=5272,glob=6,var=10
讲输出重定向到文件,./test1 >tmp.txt,然后显示文件cat tmp.txt,输出如下:
a write to stdout
pid=5274,before fork1
pid=5275,glob=7,var=11
pid=5274,before fork1
father after waitpid
pid=5274,glob=6,var=10
可见重定向到文件后多数出了下面一行,是因为缓冲的缘故:write函数没有缓冲,因此只输出一行。标准I/O(printf)是有缓冲的,如果输出到终端设备是行缓冲的(由于有\n因此缓冲区被冲洗),在终端只输出一次。标准I/O输出到文件是全缓冲的,因此在在子进程的最后一行输出后会把缓冲区内的内容输出
pid=5274,before fork1
3:僵尸进程
当父进程fork之后创建了子进程后,如果子进程先结束,并且父进程没有wait或waitpid等待子进程,那么子进程变为僵尸进程,知道父进程结束才不是僵尸进程。
如果父进程先结束,那么子进程的父进程变为init进程。init进程的子进程结束后,init进程会调用一个wait函数取得其终止状态。
上面的程序子进程很快执行完毕,在父进程执行wait之前子进程变为僵尸进程,wait执行之后就不是僵尸进程了。可以使用下面的命令来查看当前的僵尸进程。
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
#include<sys/wait.h>
pid_t wait(int * statloc);
pid_t waitpid(pid_t pid,int* statloc,int options);
两个函数返回值:若成功则返回进程ID,出错返回-1
僵尸进程的危害:僵尸进程是一个运行完毕的进程,所有的资源都已经释放了,包括打开的文件,占用的内存等等。但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等),直到父进程通过wait/waitpid来取时才释放。否则的话这些信息会一直保存,进程号就会被一直占用,但是系统所能使用的进程号是有限的,如果产生大量的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
解决僵尸进程的方法:如果一个进程fork一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态知道父进程终止,实现这一要求的技巧是调用fork两次。
代码如下:
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/wait.h>
#include<stdlib.h>
void printErr(const char* info){
write(STDERR_FILENO,info,strlen(info));
_exit(1);
}
int main(){
pid_t pid;
if((pid=fork())<0)
printErr("fork1 error\n");
else if(pid==0){
if((pid=fork())<0)
printErr("fork2 error\n");
else if(pid>0){
printf("first child,pid=%d,ppid=%d\n",getpid(),getppid());
_exit(0);
}
sleep(1);
printf("second child,pid=%d,ppid=%d\n",getpid(),getppid());
_exit(0);
}
if(waitpid(pid,NULL,0)!=pid)
printErr("waitpid error\n");
printf("parent pid=%d\n",getpid());
return 0;
}