测试代码
#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
通过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干掉子进程,这时发现 子进程号的最后出现 其代表的意思就是僵尸进程
使用 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的爷爷进程托管,僵尸进程就是子进程先于父进程先去世,去世后的子进程变为了僵尸进程,有做为标记,父进程死后为僵尸进程收尸. 守护进程则是不将相应信息显示在终端,在后台默默运行. 这个一般是我们的服务器布署时的运行模式,没有终端,将相关信息输出到日志中去.