一 僵尸进程:那些虽然已经终止的进程,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间),等待其父进程为其收尸。
1,僵尸进程是子进程比父进程先终止:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork ");
return -1;
}
if(pid > 0)
{
printf("父进程 ID :%d\n",getpid());
while(1);
}
else
{
printf("子进程 ID : %d\n",getpid());
exit(0);
}
return 0;
}
设置一个死循环让父进程一直运行,子进程先结束,使用ps -ef | grep a.out 查看进程:
产生一个<defunct>,死了的进程即僵尸进程,若要处理僵尸进程,kill子进程是不可行的,只有kill其父进程。后面也会讲到用wait和waitpid处理
[root@promote 21-进程]# ps -ef | grep a.out
root 771 5570 97 23:41 pts/1 00:00:44 ./a.out
root 772 771 0 23:41 pts/1 00:00:00 [a.out] <defunct>
root 811 775 0 23:42 pts/0 00:00:00 grep a.out
[root@promote 21-进程]# kill 772
[root@promote 21-进程]# ps -ef | grep a.out
root 771 5570 98 23:41 pts/1 00:04:00 ./a.out
root 772 771 0 23:41 pts/1 00:00:00 [a.out] <defunct>
root 852 775 0 23:45 pts/0 00:00:00 grep a.out
[root@promote 21-进程]# kill 771
[root@promote 21-进程]# ps -ef | grep a.out
root 854 775 0 23:45 pts/0 00:00:00 grep a.out
2,父进程比子进程先结束,子进程会由其祖先进程init所领养
[root@promote 21-进程]# ./a.out
子进程 ID : 892
父进程 ID :891
[root@promote 21-进程]# ps -ef | grep a.out
root 892 1 93 23:50 pts/0 00:00:05 ./a.out
root 894 775 0 23:50 pts/0 00:00:00 grep a.out
[root@promote 21-进程]# pstree
init─┬─/usr/bin/sealer
├─VGAuthService
├─a.out
二 守护进程创建:
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mon()
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork ");
return -1;
}
if(pid > 0)//父进程终止
{
exit(0);
}
//创建新会话 setsid()
//#include <unistd.h>
//pid_t setsid(void);
if(setsid() < 0)
{
return -1;//On error, -1 is returned, and errno is set.
}
//重设文件掩码 umask()
//#include <sys/types.h>
//#include <sys/stat.h>
//mode_t umask(mode_t mask);
umask(0);
//改变文件工作目录 chdir()
//#include <unistd.h>
//int chdir(const char *path);
if(chdir("/") < 0)
{
return -1;//On success,zero is returned.On error, -1 is returned
}
//关闭文件描述符(关闭标准输入,标准输出,标准错误)
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//重定向标准输入、标准输出、标准错误
open("/dev/NULL",O_RDWR); //fd 0
open("/dev/NULL",O_RDWR); //fd 1
open("/dev/NULL",O_RDWR); //fd 2
//因为守护进程没有控制终端,标准输出等也被重定向到了 /dev/null
//进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。
return 0;
}
int main()
{
mon();
while(1);
return 0;
}
[root@promote 21-进程]# ./a.out
[root@promote 21-进程]# ps -ef |grep a.out
root 1182 1 96 00:19 ? 00:00:08 ./a.out
root 1184 775 0 00:19 pts/0 00:00:00 grep a.out
进程在后台运行并未形成僵尸进程
当然系统提供的函数daemon() 可以直接创建守护进程,上述做法为了更好的了解守护进程
三 进程等待:wait waitid
父进程创建子进程后,要知道子进程什么时候被终止,是被正常终止还是不正常终止
1,wait : 如果一个子进程已经终止,并且是一个僵死进程,wait立即返回并取得该子进程的状态,否则wait使其调用者阻塞直到一个子进程终止。如果调用者阻塞并且它有多个子进程,则在其一个子进程终止时,wait就立即返回。因为wait返回终止子进程的ID,所以总能了解到是哪一个子进程终止了。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork ");
return -1;
}
if(pid > 0)
{
printf("父进程ID :%d\n",getpid());
sleep(5);
//等待子进程结束
int status;
pid_t childpid = wait(&status);
printf("成功处理一个子进程,该子进程ID :%d\n",childpid);
if(WIFEXITED(status))//若子进程正常死亡,返回一个非零值
{
printf("子进程正常死亡\n");
}
else
{
printf("非正常死亡\n");
}
}
else
{
printf("子进程ID :%d\n",getpid());
exit(0);
}
return 0;
}
当子进程先结束,父进程睡眠5秒之后执行wait函数,讲子进程正常处理
[root@promote 21-进程]# ./a.out
子进程ID :1326
父进程ID :1325
成功处理一个子进程,该子进程ID :1326
子进程正常死亡
在父进程睡眠时,查看进程会发现子进程成为僵尸进程,在5秒后wait才把该进程处理掉
[root@localhost 21-进程]# ps -ef |grep a.out
root 1325 775 0 00:38 pts/0 00:00:00 ./a.out
root 1326 1325 0 00:38 pts/0 00:00:00 [a.out] <defunct>
root 1328 5570 0 00:38 pts/1 00:00:00 grep a.out
[root@localhost 21-进程]# ps -ef |grep a.out
root 1330 5570 0 00:38 pts/1 00:00:00 grep a.out
若是子进程还在运行中,中途kill子进程:子进程非正常死亡
[root@localhost 21-进程]# ps -ef |grep a.out
root 1465 775 0 00:53 pts/0 00:00:00 ./a.out
root 1466 1465 99 00:53 pts/0 00:00:06 ./a.out
root 1468 5570 0 00:53 pts/1 00:00:00 grep a.out
[root@localhost 21-进程]# kill 1466
[root@promote 21-进程]# ./a.out
子进程ID :1466
父进程ID :1465
成功处理一个子进程,该子进程ID :1466
非正常死亡
2,waitpid:
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
参数:如果不在意结束状态值,则参数status可以设成NULL。
参数pid为欲等待的子进程识别码:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。
参数option可以为0 或下面的OR 组合
WNOHANG: 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED :如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork ");
return -1;
}
if(pid == 0)
{
printf("子进程ID :%d \n",getpid());
exit(0);
}
else if(pid > 0)
{
printf("父进程ID :%d\n",getpid());
sleep(10);
int status;
pid_t childpid = waitpid (-1,&status,WNOHANG);
//pid == -1 :等待任何一个子进程退出,此时和wait相同
printf("成功处理子进程,该子进程是 :%d,%d\n",childpid,status);
}
return 0;
}
当pid = -1时,处理结果和wait相同
[root@promote 21-进程]# ./a.out
子进程ID :1507
父进程ID :1506
成功处理子进程,该子进程是 :1507,0