linux下理解僵尸进程&孤儿进程&守护进程测试范例及源码

本文详细介绍了孤儿进程、僵尸进程和守护进程的概念,通过实例演示如何创建并观察这些进程,以及它们在系统中的行为。学习者将理解进程生命周期和父进程角色在子进程管理中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

测试代码

#define _GNU_SOURCE

#include<sched.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdint.h>
#include<sys/wait.h>
#include<sys/types.h>

typedef void (*spawn_proc_pt)(void*data);

static void work_process_cycle(void*data);//干活的函数
static void start_processes(int n);		  //几个人干活

pid_t spawn_process(spawn_proc_pt proc,void *data,char *name);	//每个干活的属性


int main(int argc,char** argv){
	
	printf("father pid:%d\n",getpid());
	
	start_processes(2);

//	printf("father is over!\n");	
	//wait(NULL);
	
	while(1) sleep(10);	
	return 0;	
}

void start_processes(int n){
	
	int i =0;
	for(i=n-1;i>=0;i--){
		
		spawn_process(work_process_cycle,(void*)(intptr_t)i,"Work process");
	}
	
}

pid_t spawn_process(spawn_proc_pt proc,void *data,char *name){
	
	//只有子进程取干活
	
	pid_t pid;
	
	pid = fork();
	
	
	switch(pid){
		
	case -1:
		fprintf(stderr,"fork() failed while spawning\"%s\"\n",name);
		return -1;
	case 0:
		proc(data);
		return 0;
	default :
		//父进程不干活
		break;
		
	}
	
	printf("start pid:%ld name:%s\n",(long int)pid,name);
	
	return pid;
	
}

void work_process_init(int worker){
	cpu_set_t cpu_affinity;
	
	CPU_ZERO(&cpu_affinity); //清零
	CPU_SET(worker%CPU_SETSIZE,&cpu_affinity);//绑定

	if(stderr,sched_setaffinity(0,sizeof(cpu_set_t),&cpu_affinity)==-1){
		
		fprintf(stderr,"sched_setaffinity failed\n");
	}
}

void work_process_cycle(void *data){
	//干活前绑定
	int worker = (intptr_t)data;
	
	work_process_init(worker);
	
	for(;;){
		sleep(10);
		printf("get pid:%ld...\n",(long int)getpid());
		
	}
	
}

孤儿进程

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。

孤儿进程测试 demo

我们创建一个父进程和两个子进程,让程序运行使用 linux 的相关进程查看命令查看相关情况.要弄懂下面的测试用例得懂得进程的相关概念


将上述代码编译运行,guer.exe 是我们要进行测试的程序 看到程序启动 父进程号位 2004 的创建了两个子进程分别位 2005 和2006
![在这里插入图片描述](https://img-blog.csdnimg.cn/6e9ec0c8f1e44b66906a21bc6ac08c5c.png

通过MobaXterm Personal Edition 在打开一个终端使用命令 ps _ef|grep guer.exer 查看对应的父进程和子进程再系统中的进程 id 号
在这里插入图片描述
使用命令 kill -9 2003 杀死父进程,再使用 ps _ef|grep guer.exer 查看进程情况,发现再杀死父进程 2003 之后 进程 id 为 2004 和 2005的父进程 id号变为了1,1代表的 init 进程,我们把它称之为 “爷爷进程” 这个比比较符合人之常情孩子的父亲走了,爷爷就接管了孩子的照看任务.
在这里插入图片描述

僵尸进程

**僵尸进程:**一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用 exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。
  在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装 SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了, 那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是 为什么系统中有时会有很多的僵尸进程。

僵尸进程测试 demo

测试代码使用和孤儿进程的相同的 c 文件代码,重新编译为 jianshi.exe 的可执行文件,执行,创建父进程 id 号 2266 子进程 id 号 2267 2268
在这里插入图片描述
这时使用命令 ps -ef|grep jianshi.exe 查看相关进程 id 号
在这里插入图片描述
进程正常执行,使用 kill -9 2267 2268干掉子进程,这时发现 子进程号的最后出现 其代表的意思就是僵尸进程
![在这里插入图片描述](https://img-blog.csdnimg.cn/03ead2d059bd4f09b8e42d8b19ffb710.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaWNlX2VsZXBoYW50,size_20,color_FFFFFF,t_70,g_se,x_16
使用 kill -9 2266干掉父进程,再查看相关进程,发现父进程被杀死的时,进行了==“收尸”==,查看父进程子进程全部被杀死掉
在这里插入图片描述

守护进程

守护进程: 不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务。守护进程脱离于终端,是为了避免进程在执行过程中的信息在任何终端上显示,并且进程也不会被任何终端所产生的终端信息所打断(比如关闭终端等)。那如何成为一个守护进程呢? 步骤如下:
1.调用fork(),创建新进程,它会是将来的守护进程.
2.在父进程中调用exit,保证子进程不是进程组长
3.调用setsid()创建新的会话区
4.将当前目录改成根目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
5.将标准输入,标准输出,标准错误重定向到/dev/null.

守护进程测试 demo

进程守护使用接口 API 实现

int daemon(int nochdir, int noclose)
{
    int fd;

    switch (fork()) {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(0);
    }

    if (setsid() == -1)
        return (-1);

    if (!nochdir)
        (void)chdir("/");

    if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
        (void)dup2(fd, STDIN_FILENO);
        (void)dup2(fd, STDOUT_FILENO);
        (void)dup2(fd, STDERR_FILENO);
        if (fd > 2)
            (void)close (fd);
    }
    return (0);
}

使用时只需调用 daemon,并且传入参数 (0,0)
在这里插入图片描述
运行程序 cp.exe 发现程序中 printf 打印的字符并未出现在终端上出现
在这里插入图片描述
我们再打开另一个终端查看 cp.exe 这个进程是否存在,发现进程 id 确实存在,说明进程在运行
在这里插入图片描述
我们使用 killall -9 cp.exe 杀死这个进程看看,进程可以被杀死
在这里插入图片描述
总结:孤儿进程就是父进程再子进程前去世去世后的子进程有 线程 id 号为1的爷爷进程托管,僵尸进程就是子进程先于父进程先去世,去世后的子进程变为了僵尸进程,有做为标记,父进程死后为僵尸进程收尸. 守护进程则是不将相应信息显示在终端,在后台默默运行. 这个一般是我们的服务器布署时的运行模式,没有终端,将相关信息输出到日志中去.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值