11进程控制2
同步父子进程 wait
<sys/wait.h>
pid_t wait(int *pState)
无子进程则马上返回,有子进程则等待(阻塞)。多个子进程,需要多个调用
获取子进程 pid 和 结束状态
第一字节: *pState & 0xff--------接受到的信号值
第二字节: (*pState >> 8) & 0xff-------退出码
第三四字节保留:0
同步父子进程 waitpid
waitpid(pid_t pid, int *pState, int option)
参数解析:
pid
>0 等待特定子进程
=0 等待父进程对应进程组的所有子进程(任意一个触发退出)
=-1 等待任意子进程
<-1 等待进程组标志为 绝对值|pid| 的所有子进程
option
0------有子进程则阻塞,等待子进程结束
1[WNOHANG]-----非阻塞调用,立即返回(wait no hang),搜集已经退出的子进程,如果要搜集多个子进程的,需要多次调用
2[WUNTRACED]----有子进程则阻塞,但是如果waitpid之前有子进程退出,则搜集已退出的子进程并返回
同步进程
pid = waitpid(-1, pState, 0) 等价于
pid = wait(pState)
都可以搜集已经退出的子进程
例子:
void testWait()
{
pid_t pid1,pid2;
printf("start fork:%d\n",getpid());
pid1=fork();
if(pid1==0)
{
printf("this is child1 [%d]\n",getpid());
exit(1);
}
pid2=fork();
if(pid2==0)
{
printf("this is child2 [%d]\n",getpid());
exit(2);
}
int state;
pid_t pid=wait(&state);
if(pid>0)
{
printf("Signal:%d,Exit:%d\n",state&0xff,(state>>8)&0xff);
}
//多个子进程,多个wait
wait(NULL);
printf("end fork :%d\n",getpid());
}
kill 杀死进程
shell 命令
kill –l 查看kill 信号
kill -[sig] pid 结束程序
函数命令
int kill(pid_t pid, int sig)
如:
kill 4000(进程号)
kill -9(信号) 4000(进程号)
例子:
void testKill()
{
pid_t pid1,pid2;
pid1=fork();
if(pid1==0)
{
pid2=fork();
if(pid2==0)
{
printf("kill parent!\n");
kill(getppid(),9);
exit(0);
}
else
{
sleep(1);
}
exit(0);
}
int state;
pid_t pid=wait(&state);
if(pid>0)
{
printf("Signal:%d,Exit:%d\n",state&0xff,(state>>8)&0xff);
}
补充:
sleep(N) //睡眠N秒钟
usleep(N) //睡眠N微秒 (1秒=1000毫秒 = 10^6微秒)
僵尸进程
概念:
进程已经终止,但是没有从进程表内删除。僵尸进程已经终止,所以不能接受 kill 信号
产生原因:
子进程终止时,会释放资源,并发送SIGCHLD信号通知父进程; 父进程接受到信号后调用wait返回子进程状态,并且释放进程表资源
如果子进程结束时,父进程没有调用wait接受子进程信息,则子进程转化为僵尸进程,父进程退出时,僵尸进程自动结束
例子:
void testZomb()
{
pid_t pid=fork();
if(pid==0)
{
printf("this is a child :%d",getpid());
exit(0);
}
else
{
printf("this is a parent :%d",getpid());
while(1);
}
}
rowe 3440 3110 92 23:04 pts/0 00:00:08 ./main
rowe 3441 3440 0 23:04 pts/0 00:00:00 [main] <defunct>
<defunct>说明就是僵尸进程,并且无法使用kill命令进行终止,也无法对其进行任何操作。
僵尸进程解决方案
1: wait方法 (缺点:父进程阻塞)
2:托管
父进程先于子进程退出,则它的子进程由init进程领养,子进程的父进程ID变为1,由init释放进程表资源.
托管技巧:fork出子进程后,在子进程中再次fork,然后退出子进程,则子子进程会被init托管,由init释放资源表
3:忽略SIGCHLD信号
父进程设置忽略 SIGCHLD信号,子进程结束自动释放进程表资源
忽略SIGCHLD信号:
signal(SIGCHLD, SIG_IGN)
4:捕获SIGCHLD信号
父进程捕获SIGCHLD信号,并在捕获函数代码中 执行wait()
处理SIGCHLD信号:
void pFun(int nSignal)
signal(SIGCHIL, pFun)
wait方法:
void testZomb()
{
pid_t pid=fork();
if(pid==0)
{
printf("this is a child :%d",getpid());
exit(0);
}
else
{
printf("this is a parent :%d",getpid());
wait(NULL);
while(1);
}
}
托管方法:
void testTrust()
{
pid_t pid=fork();
if(pid==0)
{
printf("this is a child :%d\n",getpid());
pid_t pidd=fork();
if(pidd==0)
{
printf("this is a sub child :%d\n",getpid());
//可以其他操纵
//exec()...............
//退出子子进程
exit(0);
}
else
{
//退出子进程
exit(0);
}
}
else
{
//回收资源表,父进程退出
wait(NULL);
printf("this is a parent :%d\n",getpid());
while(1);
}
}
守护进程 daemon,也称精灵进程:是一种运行在后台的特殊进程,不存在控制终端,周期性处理某项任务,守护系统正常运行,大部分socket通信服务程序都以守护进程方式执行。
以超级用户启动的(uid=0)
父进程为 init (ppid=1)
无控制终端 (tty = ?)
终端进程组为 -1 (TPGID=-1)
守护进程 daemon 创建步骤
1: 后台运行
拖管法,fork子进程后,父进程退出
2: 脱离控制终端(伪终端)
pid = setsid()
创建一个新的session和进程组;
并使用进程ID作为进程组ID,返回进程组ID / 失败返回(-1)
3: 把当前工作目录更改为 /
防止对当前目录的某些操作不能执行, chdir()
4: 关闭文件描述符,重定向 stdin stdout stderr
fd=open(“/dev/null”, O_RDWR, 0);
dup2(fd, STDIN_FILENO); ...
5: 设置/清除文件创建掩码 <sys/stat.h>
umask(XXX) 可以设置进程创建的临时文件不被其他用户查看
umask(0) 清除从父进程继承的文件创建掩码
例子:
void testDaemon()
{
pid_t pid=fork();
//step1:托管,退出父进程
if(pid>0)
{
exit(0);
}
//step2:脱离终端
pid=setsid();
//step:更改目录,可省略
//chdir("/home");
//step4 关闭文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//利用子进程打开文件
pid=fork();
if(pid==0)
{
//gedit代表文本编辑器,123.txt自拟文本
execlp("gedit","gedit","123.txt",NULL);
}
//execlp结束后,wait启动,立即重新打开
while(1)
{
wait(NULL);
pid=fork();
if(pid==0)
{
execlp("gedit","gedit","123.txt",NULL);
}
}
}