父进程先于子进程退出,子进程失去托管,这种子进程统称为孤儿进程
getpid();
getppid();
ps aux #查看进程详细信息
ps ajx #查看进程关系
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
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;
}
为了方便管理系统中大量进程,设计了进程组结构,属于一种管理概念
进程组是由一个组长进程和多个组员进程构成、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); //转移进程到目标组中去
转移进程要保证对目标组有足够的访问权限,其次目标组要存在
会话关系便于终端或系统管理终端进程和终端子进程
会话由一个会话发起者和多个会话参与者构成
会话发起者退出,以进程组为单位杀死参与者
只杀死终端进程为首的一组进程
会话发起者的标志 pid == pgid == sid(会话id)
后续开发app时,要进行脱离控制终端,避免会话发起者bash接收,杀死应用进程
脱离终端:让组员进程创建新组,避免杀死
创建新会话脱离原有会话
getsid(getpid());//获取当前进程的会话id
setsid();//当前进程创建新会话,创建新会话只有组员进程能完成,因为此函数中会进行进程组创建,如果是组长此步骤不会成功
终端进程无法脱离终端,必然受终端控制,因为它无法创建组也无法成立新会话
孤儿进程,多进程模型中父进程异常关闭,导致子进程失去托管,这类进程为孤儿进程。
开发者关闭其父进程,让子进程脱离终端控制,此进程工作于后台,周期执行,这类进程称为守护进程(也是孤儿)
守护进程的生命周期比普通进程长,守护进程的生命周期随操作系统持续,开机自动启动,关机则关闭
守护进程为主程序提供服务和支持,保证主程序的稳定性
守护进程不允许参与前台任务,也不允许将数据打印到前台
低效模式运行,不允许占用大量系统资源
造孤儿——父进程创建子进程、父进程退出
子进程脱离控制终端,创建新会话
关闭前台描述符STDIN_FILENO,STDOUT_FILENO,
修改进程的工作目录为根目录
默认情况下进程的工作目录,是程序所在位置,进程访问文件,访问磁盘信息都是以工作目录为基准
修改进程的Umask掩码,为0002
执行守护进程任务
守护进程的退出处理