其实有时想想linux内核的设计也蕴含着很多人生哲学,在linux中有这么几个特殊进程中,我们一开始见到它们的名字可能还会觉得很诧异,但在了解完了原理后,我们仔细想想,这样的命名也不无道理!下面我就给大家分别介绍一下这三种特殊的进程!
1.孤儿进程
如果父进程先退出,子进程还没退出那么子进程将被 托孤给init进程,这是子进程的父进程就是init进程(1号进程).其实还是很好理解的.
#include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <errno.h> #include <signal.h> int main(void) { pid_t pid ; signal(SIGCHLD,SIG_IGN); printf("before fork pid:%d\n",getpid()); int abc = 10; pid = fork(); if(pid == -1) { perror("tile"); return -1; } if(pid > 0) //父进程先退出 { abc++; printf("parent:pid:%d \n",getpid()); printf("abc:%d \n",abc); sleep(5); } else if(pid == 0){ //值进程后退出,被托付给init进程 abc++; printf("child:%d,parent: %d\n",getpid(),getppid()); printf("abc:%d",abc); sleep(100); } printf("fork after...\n"); }
before fork pid:27709
parent:pid:27709
abc:11
child:27710,parent: 27709
fork after...
disda 27710 1 0 10:47 pts/1 00:00:00 ./review
disda 27713 25948 47 10:47 pts/3 00:00:00 ps -ef
我们执行程序后由于子进程进入sleep(100),而父进程先退出.通过ps -ef命令我们可以知道,此时27710号进程的父进程编程了1号进程.也就是我们所说的init进程.
2.僵尸进程
如果我们了解过linux进程状态及转换关系,我们应该知道进程这么多状态中有一种状态是僵死状态,就是进程终止后进入僵死状态(zombie),等待告知父进程自己终止,后才能完全消失.但是如果一个进程已经终止了,但是其父进程还没有获取其状态,那么这个进程就称之为僵尸进程.僵尸进程还会消耗一定的系统资源,并且还保留一些概要信息供父进程查询子进程的状态可以提供父进程想要的信息.一旦父进程得到想要的信息,僵尸进程就会结束.
int main(void) { pid_t pid ; //signal(SIGCHLD,SIG_IGN); printf("before fork pid:%d\n",getpid()); int abc = 10; pid = fork(); if(pid == -1) { perror("tile"); return -1; } if(pid > 0) { abc++; printf("parent:pid:%d \n",getpid()); printf("abc:%d \n",abc); sleep(20); } else if(pid == 0){ abc++; printf("child:%d,parent: %d\n",getpid(),getppid()); printf("abc:%d",abc); exit(0); } printf("fork after...\n");
disda 27881 23047 0 11:12 pts/1 00:00:00 ./fork01
disda 27882 27881 0 11:12 pts/1 00:00:00 [fork01] <defunct>
同样通过ps -ef我们可以得知进程信息和进程pid,可以看到子进程就是处于defunct状态.这时我们肯定想要怎么才能避免僵尸进程呢?看程序被我注释的那句signal(SIGCHLD,SIG_IGN),加上就不会出现僵尸进程了.那我们就加点篇幅讲一下为什么就可以避免僵尸进程呢?
这是signal()函数的声明sighandler_t signal(int signum, sighandler_t handler),我们可以得出,signal函数的第一个函数是Linux支持的信号,第二个参数是对信号的操作 ,是系统默认还是忽略或捕获.我们这是就可以知道signal(SIGCHLD,SIG_IGN)是选择对子程序终止信号选择忽略,这是僵尸进程就是交个内核自己处理,并不会产生僵尸进程.
3.守护进程
同样我们需要了解一下什么是守护进程,守护进程就是在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务.习惯上守护进程的名字通常以d结尾(sshd),但这些不是必须的.
下面介绍一下创建守护进程的步骤
- 调用fork(),创建新进程,它会是将来的守护进程.
- 在父进程中调用exit,保证子进程不是进程组长
- 调用setsid()创建新的会话区
- 将当前目录改成跟目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
- 将标准输入,标注输出,标准错误重定向到/dev/null
#include <sys/types.h> #incldue <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #incldue <unistd.h> #include <linux/fs.h> int main(void) { pid_t pid; int i; pid = fork(); //创建一个新进程,将来会是守护进程 if(pid == -1) { return -1; } else if(pid != 0){ //父进程调用exit,保证子进程不是进程组长 exit(EXIT_SUCCESS); } if(setsid() == -1) //创建新的会话区 { return -1; } if(chdir("/") == -1) //将当前目录改成根目录 { return -1; } for(i = 0;i < NR_OPEN;i++) { close(i); } open("/dev/null",O_RDWR); 重定向 dup(0); dup(0); return 0; }
disda 26217 1 0 06:59 ? 00:00:00 ./dm01_demon 则出现了守护进程!