linux编程-守护进程编写 守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端而且周期性地执行某种任务或等待 处理某些发生的事件。守护进程是一种颇有用的进程。 Linux的大多数服务器就是用守护进程实现的。好比,Internet服务器inetd,Web服务器httpd等。 同时,守护进程完成许多系统任务。好比,做业规划进程crond,打印进程lpd等。 守护进程的编程自己并不复杂,复杂的是各类版本的Unix的实现机制不尽相同, 形成不一样 Unix环境下守护进程的编程规则并不一致。 须要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。 下面结合一些前辈的文档和本身的例子说说守护进程的编程。 .基本概念 .进程 .每一个进程都有一个父进程 .当子进程终止时,父进程会获得通知并能取得子进程的退出状态。 .进程组 .每一个进程也属于一个进程组 .每一个进程主都有一个进程组号,该号等于该进程组组长的PID号 .一个进程只能为它本身或子进程设置进程组ID号 .会话期 .对话期(session)是一个或多个进程组的集合。 .setsid()函数能够创建一个对话期: 若是,调用setsid的进程不是一个进程组的组长,此函数建立一个新的会话期。 (1)此进程变成该对话期的首进程 (2)此进程变成一个新进程组的组长进程。 (3)此进程没有控制终端,若是在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。 若是该进程是一个进程组的组长,此函数返回错误。 (4)为了保证这一点,咱们先调用fork()而后exit(),此时只有子进程在运行, 子进程继承了父进程的进程组ID,可是进程PID倒是新分配的,因此不多是新会话的进程组的PID。 从而保证了这一点。 if((pid=fork())>0) //parent exit(0); else if(pid==0){ //th1 child setsid(); //th1是成为会话期组长 if(fork() ==0){ //th2不会是会话期组长(变成孤儿进程组) ... } } 一. 守护进程及其特性 (1)守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之类似。 (2)其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端, 会话和进程组,工做目录以及文件建立掩模等。这些环境一般是守护进程从执行它的父进程(特别是shell) 中继承下来的。 (3)最后,守护进程的启动方式有其特殊之处。它能够在Linux系统启动时从启动脚本/etc/rc.d中启动, 能够由做业规划进程crond启动,还能够由用户终端(一般是 shell)执行。 总之,除开这些特殊性之外,守护进程与普通进程基本上没有什么区别。 所以,编写守护进程其实是把一个普通进程按照上述的守护进程的特性改形成为守护进程。 二. 守护进程的编程要点 (来自UEAP) 前面讲过,不一样Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都同样, 区别在于具体的实现细节不一样。这个原则就是要知足守护进程的特性。 同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点以下; 1. 在后台运行。 为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止, 让Daemon在子进程中后台执行。 if(pid=fork()) exit(0); //是父进程,结束父进程,子进程继续 2. 脱离控制终端,登陆会话和进程组 进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登陆会话能够包含多个进程组。 这些进程组共享一个控制终端。这个控制终端一般是建立进程的登陆终端。 控制终端,登陆会话和进程组一般是从父进程继承下来的。 咱们的目的就是要摆脱它们,使之不受它们的影响。 方法是在第1点的基础上,调用setsid()使进程成为会话组长: setsid(); 说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。 setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登陆会话和进程组脱离。 因为会话过程对控制终端的独占性,进程同时与控制终端脱离。 3. 禁止进程从新打开控制终端 如今,进程已经成为无终端的会话组长。但它能够从新申请打开一个控制终端。 能够经过使进程再也不成为会话组长来禁止进程从新打开控制终端: if(pid=fork()) exit(0); //结束第一子进程,第二子进程继续(第二子进程再也不是会话组长) 4. 关闭打开的文件描述符 进程从建立它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源, 形成进程所在的文件系统没法卸下以及引发没法预料的错误。按以下方法关闭它们: for(i=0;i 关闭打开的文件描述符close(i);> 5. 改变当前工做目录 进程活动时,其工做目录所在的文件系统不能卸下。通常须要将工做目录改变到根目录。 对于须要转储核心,写运行日志的进程将工做目录改变到特定目录如 /tmpchdir("/") 6. 重设文件建立掩模 进程从建立它的父进程那里继承了文件建立掩模。它可能修改守护进程所建立的文件的存取位。 为防止这一点,将文件建立掩模清除:umask(0); 7. 处理SIGCHLD信号 处理SIGCHLD信号并非必须的。 但对于某些进程,特别是服务器进程每每在请求到来时生成子进程处理请求。 若是父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。 若是父进程等待子进程结束,将增长父进程的负担,影响服务器进程的并发性能。 在Linux下能够简单地将 SIGCHLD信号的操做设为SIG_IGN。 signal(SIGCHLD,SIG_IGN); 这样,内核在子进程结束时不会产生僵尸进程。 这一点与BSD4不一样,BSD4下必须显式等待子进程结束才能释放僵尸进程。 三. 守护进程实例 守护进程实例包括两部分:主程序test.c和初始化程序init.c。 主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。 初始化程序中的init_daemon函数负责生成守护进程。读者能够利用init_daemon函数生成本身的守护进程。 //------------------------------------------------------------------------
#include<unistd.h>
#include<signal.h> #include<stdio.h> #include<stdlib.h> #include<sys/param.h> #include<sys/types.h> #include<sys/stat.h> #include<time.h> void init_daemon() { int pid; int i; pid=fork(); if(pid<0) exit(1); //建立错误,退出 else if(pid>0) //父进程退出 exit(0); setsid(); //使子进程成为组长 pid=fork(); if(pid>0) exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端 else if(pid<0) exit(1); //关闭进程打开的文件句柄 for(i=0;i<NOFILE;i++) close(i); chdir("/root/test"); //改变目录 umask(0);//重设文件建立的掩码 return; } void main() { FILE *fp; time_t t; init_daemon(); while(1) { sleep(60); //等待一分钟再写入 fp=fopen("testfork2.log","a"); if(fp>=0) { time(&t); fprintf(fp,"current time is:%s\n",asctime(localtime(&t))); //转换为本地时间输出 fclose(fp); } } return; }
运行下面的命令:linux
cc testfork2.c -o testfork2shell
./testfork2编程
ps -ef|grep testfork2 能够查找到对应的进程服务器
kill -9 1231杀死进程session
转(参考):http://blog.csdn.net/zg_hover/article/details/2553321并发