06进程间关系-学习笔记
Orphan Process孤儿进程
父进程先于子进程退出,子进程失去托管,这种子进程统称为孤儿进程
- 失效进程(孤儿进程):导致内存泄漏,影响新进程的创建
- 孤儿进程的危害不可预测,如果一个孤儿进程持续的申请系统资源,这导致影响系统稳定性
编写一个孤儿进程检测处理模型
getpid();
getppid();
ps aux #查看进程详细信息
ps ajx #查看进程关系
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid;
pid = fork();
if (pid > 0)
{
sleep(30);
exit(0);
}
else if (pid == 0)
{
printf("child pid = %d ,ppid = %d", getpid(), getppid());
sleep(32);
printf("child pid = %d ,ppid = %d", getpid(), getppid());
while(1)
{
sleep(1);
}
}
else
{
}
return 0;
}
孤儿进程检查
创建一个独立的check进程。父进程通过管道将自己的pid传给check进程。
process.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
int main()
{
// 创建五个子进程
pid_t pid;
int i = 0;
for (i; i < 5; i++)
{
pid = fork();
if (pid == 0)
break;
}
// 父进程
if (pid > 0)
{
int wfd;
char str_pid[10];
bzero(str_pid, 10);
if ((wfd = open("fifo", O_RDWR)) == -1)//两端都是RDWR权限,否则会check端会因为process端结束而发现管道关闭,出现异常。
{
perror("open failed");
exit(0);
}
sprintf(str_pid, "%d", getpid());
write(wfd, str_pid, strlen(str_pid));
sleep(20);
exit(0);
wait(NULL);
}
else if (pid == 0)
{
printf("child pid %d running..\n", getpid());
while (1)
{
sleep(1);
}
}
else
{
perror("fork call failed");
exit(0);
}
return 0;
}
check.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <errno.h>
int main()
{
int rfd;
if ((rfd = open("fifo", O_RDWR)) == -1)//使用RDWR权限,防止写端关闭,管道权限不够而关闭
{
perror("打开管道失败");
exit(0);
}
printf("open成功\n");
char buf[10];
bzero(buf, sizeof(buf));
read(rfd, buf, sizeof(buf));
pid_t pid = atoi(buf);
printf("check process pid: %d ,get Parent pid: %d..\n", getpid(), pid);
// 非阻塞读取管道
// 1、获取文件属性
int flag;
fcntl(rfd, F_GETFL, &flag);
flag |= O_NONBLOCK; // 设置文件属性为非阻塞
fcntl(rfd, F_SETFL, flag);
int len;
int esrch;
char str_pid[10];
bzero(str_pid, 10);
while ((len = read(rfd, buf, sizeof(buf))) == -1)
{
// EAGAIN:这意味着“现在没有可用的数据,以后再试一次” 。
//非阻塞返回
if (errno == EAGAIN)
{
kill(pid, 0); // 尝试杀一下这个进程
if (errno == ESRCH)
{
// 要杀的进程已经不存在了
printf("check parent %d its dead\n", pid);
pid -= pid * 2;
// sprintf(str_pid,"%d",pid);
printf("group id %d\n", pid);
// 杀死孤儿进程
kill(pid, 9);//函数kill比重载kill命令更合理
exit(0);
}
printf("check parent alive\n");
}
else
{
perror("read call failed");
exit(0);
}
sleep(1);
}
if (len > 0)
{
printf("parent exit , check_process Done\n");
exit(0);
}
return 0;
}
注意要点
- 双端访问权限都要是RDWR
- “通过尝试kill父进程,来判断父进程是否存在”,不要放在判断语句里。判断errno的值
- kill子进程组是,要使用函数而不是重载命令
进程间三种关系
亲缘关系
process Group进程组关系
-
为了方便管理系统中大量进程,设计了进程组结构,属于一种管理概念
-
进程组是由一个组长进程和多个组员进程构成、PID(Process ID)、PPID(Parent Process ID)和PGID(Process Group ID)
-
每个终端进程都是组长进程
-
组长进程标志,pid == pgid,此进程为组长进程
-
进程组的生命周期较长,与某个特定的进程无关,直到组中最后一个进程终止或转移,进程组为空时,系统释放进程组
-
根据就近原则,组长进程创建的子进程,这些子进程会归纳到父进程同组变为组员进程
-
进程组关系与亲缘关系没有必然联系,因为组成员可以转移的
-
创建进程组只有组员进程可以完成,组长无法成功
getpgrp(); //返回当前进程的pgid(组id) setpgid(pid_tpid,pid t gpid); //此函数可以创建组或转移组中进程 setpgid(getpid(),getpid()); //申请组id,创建组,只有组员进程可以成功 setpgid(getpid(0,1000); //转移进程到目标组中去
-
转移进程要保证对目标组有足够的访问权限,其次目标组要存在
process Session会话关系
-
会话关系便于终端或系统管理终端进程和终端子进程
-
会话由一个会话发起者和多个会话参与者构成
-
会话发起者退出,以进程组为单位杀死参与者
-
只杀死终端进程为首的一组进程
-
会话发起者的标志 pid == pgid == sid(会话id)
-
后续开发app时,要进行脱离控制终端,避免会话发起者bash接收,杀死应用进程
-
脱离终端:让组员进程创建新组,避免杀死
-
创建新会话脱离原有会话
getsid(getpid());//获取当前进程的会话id setsid();//当前进程创建新会话,创建新会话只有组员进程能完成,因为此函数中会进行进程组创建,如果是组长此步骤不会成功
-
终端进程无法脱离终端,必然受终端控制,因为它无法创建组也无法成立新会话
-
另一种孤儿进程——守护进程Daemon Process
孤儿进程,多进程模型中父进程异常关闭,导致子进程失去托管,这类进程为孤儿进程。
开发者关闭其父进程,让子进程脱离终端控制,此进程工作于后台,周期执行,这类进程称为守护进程(也是孤儿)
守护进程(也叫精灵进程)和普通进程的差异性
-
守护进程的生命周期比普通进程长,守护进程的生命周期随操作系统持续,开机自动启动,关机则关闭
-
守护进程为主程序提供服务和支持,保证主程序的稳定性
-
守护进程不允许参与前台任务,也不允许将数据打印到前台
-
低效模式运行,不允许占用大量系统资源
守护进程执行的三种模式
- 间隔执行
- 定时执行
- 条件触发
shell脚本实现开机启动
守护进程的开发流程
-
重定向STD_FILENO
-
造孤儿——父进程创建子进程、父进程退出
-
子进程脱离控制终端,创建新会话
-
关闭前台描述符STDIN_FILENO,STDOUT_FILENO,
- STD_FILENO默认是perror(“xxx call faild”) 标准错误是占用标准输出的,会将异常信息显示在终端上(前台)
- 为了避免错误信息抛出到前台,可以采用重定向的方式,将错误信息抛出到文件中
-
修改进程的工作目录为根目录
默认情况下进程的工作目录,是程序所在位置,进程访问文件,访问磁盘信息都是以工作目录为基准
-
修改进程的Umask掩码,为0002
-
执行守护进程任务
-
守护进程的退出处理
demo,编写一个守护进程,后台执行,开机启动,每间隔3s向time.log中写入系统时间
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!