在Linux或者Unix操做系统中在系统引导的时候会开启不少服务,这些服务就叫作守护进程。linux
守护进程,也就是一般说的Daemon进程,是Linux中的后台服务进程。sql
它是一个生存期较长的进程,一般独立于控制终端而且周期性地执行某种任务或等待处理某些发生的事件。shell
守护进程经常在系统引导装入时启动,在系统关闭时终止。数据库
Linux系统有不少守护进程,大多数服务都是经过守护进程实现的,如Internet服务器inetd,Web服务器httpd等;缓存
同时,守护进程还能完成许多系统任务,例如,做业规划进程crond、打印进程lqd等(这里的结尾字母d就是Daemon的意思)。服务器
因为在Linux中,每个系统与用户进行交流的界面称为终端,每个今后终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端。网络
当控制终端被关闭时,相应的进程都会自动关闭。可是守护进程却可以突破这种限制,它从被执行开始运转,直到整个系统关闭时才退出。session
若是想让某个进程不由于用户或终端或其余地变化而受到影响,那么就必须把这个进程变成一个守护进程。socket
守护进程也可能从某个终端由用户在shell提示符下键入命令行启动,这样的守护进程必须亲自脱离与控制终端的关联,ide
从而避免与做业控制、终端会话管理、终端产生信号等发生任何不指望的交互,也可避免在后台运行的守护进程非预期地输出到终端。
Linux守护进程列表:
#include <syslog.h>
void syslog(int priority, const char *message, ...); // 记录至系统日志
第一个参数priority是级别(level)和设施(facility)二者的组合;
第二个参数message相似printf的格式串。
日志消息的level:
日志消息的facility:
例如rename函数调用意外失败时,守护进程能够执行如下调用:
syslog(LOG_INFO | LOG_LOCAL2, "rename(%s, %s): %m", file1, file2);
level和facility的目的在于,容许在/etc/syslog.conf文件中统一配置来自同一个给定设施的全部消息,
或者统一配置具备相同级别的全部消息。举例来讲,该配置文件可能含有如下两行:
kern.* /dev/console
local7.debug /var/log/cisco.log
这两行指定全部内核消息登记到控制台,来自local7设施的全部debug消息添加到文件/var/log/cisco.log的末尾。
当syslog被应用进程首次调用时,它建立一个Unix域数据报套接字,而后调用connect链接到由syslogd守护进程建立的
Unix域数据报套接字的众所周知路径名(如/var/run/log)。这个套接字一直保持打开,指导进程终止为止。
做为替换,进程也能够调用openlog和closelog。
#include <syslog.h>
void openlog(const char *ident, int options, int facility); // 可在首次调用syslog前调用
void closelog(void); // 可在应用进程再也不须要发送日志消息时调用
参数ident是一个由syslog冠于每一个日志消息前的字符串,其值一般为程序名。
参数options由一个或多个常值的逻辑或构成。
openlog被调用时,一般并不当即建立Unix域套接字;相反,该套接字直到首次调用syslog时才打开。
LOG_NDELAY选项迫使该套接字在openlog被调用时就建立。
日志消息也能够由logger命令产生,可用在shell脚本中以向syslogd发送消息。
Linux建立守护进程的步骤:
1)建立子进程,父进程退出
2)在子进程中建立新会话
3)改变当前目录为根目录
4)重设文件权限掩码
5)关闭文件描述符
6)忽略SIGCHLD信号
7)用日志系统记录出错信息
8)守护进程退出处理
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<fcntl.h> #include<sys/types.h> #include<unistd.h> #include<sys/wait.h> #define MAXFILE 65535 void sigterm_handler(int arg); volatile sig_atomic_t _running = 1; int main() { pid_t pc, pid; int i, fd, len, flag = 1; char *buf = "this is a Dameon\n"; len = strlen(buf); pc = fork(); //第一步 if(pc < 0){ printf("error fork\n"); exit(1); } else if(pc > 0) exit(0); pid = setsid(); //第二步 if (pid < 0) perror("setsid error"); chdir("/"); //第三步 umask(0); //第四步 for(i = 0; i < MAXFILE; i++) //第五步 close(i); signal(SIGTERM, sigterm_handler); while(_running){ if( flag == 1 && (fd=open("/tmp/daemon.log", O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0){ perror("open"); flag=0; exit(1); } write(fd, buf, len); close(fd); usleep(10 * 1000); //10毫秒 } } void sigterm_handler(int arg) { _running = 0; }
#include "unp.h" #include <syslog.h> #define MAXFD 64 extern int daemon_proc; int daemon_init(const char *pname, int facility) { int i; pid_t pid; if ( (pid = fork()) < 0) return -1; else if (pid) exit(0); if (setsid() < 0) return -1; signal(SIGHUP, SIG_IGN); if ( (pid = fork()) < 0) return -1; else if (pid) exit(0); daemon_proc = 1; // 把全局变量daemon_proc置为非0值,告知它们改成调用syslog,以取代fprintf到标准错误输出。 chdir("/"); // 把工做目录改到根目录,守护进程多是在某个任意的文件系统中启动,若仍在其中,该文件系统就没法拆卸,除非采用潜在破坏性的强制措施。 for (i = 0; i < MAXFD; i++) close(i); // 关闭本守护进程从执行它的进程(一般是一个shell)继承来的全部打开的描述符 open("/dev/null", O_RDONLY); // 将stdin、stdout和stderr重定向到/dev/null open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); openlog(pname, LOG_PID, facility); // 使用syslogd处理错误,第一个参数来自调用者,一般为程序名字(argv[0])。
// 第二个参数指定把进程ID加到每一个日志消息中。
// 第三个参数由调用者指定。 return 0; }
首次调用fork终止父进程。本进程是从前台做为一个shell命令启动的,当父进程终止时shell认为该命令已执行完毕。这样子进程就自动在后台运行。
另外,子进程继承了父进程的进程组ID,不过它有本身的进程ID,这就保证子进程不是一个进程组的头进程,则该接着调用setsid。
setsid用于建立一个新的会话(session)。当前进程变为新会话的会话头进程以及新进程组的进程组头进程,从而再也不有控制终端。
忽略SIGHUP信号并再次调用fork。再次调用fork目的是确保本守护进程未来即便打开一个终端设备,也不会自动得到控制终端。(确保新的子进程再也不是一个会话头进程)
这里必须忽略SIGHUP信号,由于当会话头进程(即首次fork产生的子进程)终止时,其会话中的全部进程(即再次fork产生的子进程)都收到SIGHUP信号。
int main(int argc, char **argv) { int listenfd, connfd; socklen_t addrlen, len; struct sockaddr_in servaddr; char buff[MAXLINE]; pid_t pid; time_t ticks; daemon_init(argv[0], 0); if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ cout<<"Error No:"<<errno<<endl; cout<<"socket error!"<<endl; exit(0); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(30000); if (bind(listenfd, (SA*)&servaddr, sizeof(servaddr)) < 0){ cout<<"bind error!"<<endl; exit(0); } if (listen(listenfd, LISTENQ) < 0){ cout<<"listen error!"<<endl; exit(0); } for ( ; ; ){ connfd = accept(listenfd, (SA*)NULL, NULL); if ( (pid = fork()) == 0){ close(listenfd); ticks = time(NULL); bzero(buff, sizeof(buff)); sprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); //str_echo(connfd); exit(0); } close(connfd); } return 0; }