守护进程(daemon)是长期存在的一种进程-->在系统引导启动时装入,仅在系统关闭时终止
该版本的Linux使用一个名为 kthreadd 的特殊内核进程来创建其他内核进程,所以 kthreadd 表现为其他内核进程的父进程
创建守护进程 code 13_1
/*
创建守护进程
output:
kali@kali:~/Desktop/Linux Study/Hellos/Chapter13$ ps -ajx |grep 7014
1 7014 7013 7013 ? -1 S 1000 0:00 ./Daemon_13_1
6592 7298 7297 6592 pts/4 7297 S+ 1000 0:00 grep 7014
可以看到守护进程为父进程fork()后的子进程,父进程exit后又init进程接管(ppid = 1)
*/
#include "apue.h"
#include
#include
#include
void deamonize(const char*cmd){
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
printf("start daemonize..\n");
/*
Clear file creation mask
*/
umask(0);
/*
Get maximum number of file descriptors
*/
if(getrlimit(RLIMIT_NOFILE,&rl)<0)
printf("%s: cannot get file limit",cmd);
/*
Become a session leader to lose controlling TTY
*/
if((pid = fork())<0)
printf("%s: can't fork\n",cmd);
else if(pid!=0)
exit(0);
setsid();/* Create a new session with the calling process as its leader. */
/*
Ensure future opens won't allocate controlling TTYs.
*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);/* 解除所有信号屏蔽 */
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL)<0){
printf("%s: can't ignore SIGHUP\n",cmd);
}
if((pid = fork())<0)
printf("%s: cannot fork",cmd);
else if(pid!=0)
exit(0);
/*
Change the current working directory to the root so
we won't prevent file systems from being unmounted
*/
if(chdir("/")<0)
printf("%s: cannot change directory to /",cmd);
/*
Close all file descriptor,
使得守护进程不再持有从其父进程继承来的任何文件描述符
*/
if(rl.rlim_max == RLIM_INFINITY){
rl.rlim_max = 1024;
}
for (int i = 0; i < rl.rlim_max; i++)
{
close(i);
}
/*
Attach file descriptors 0,1,2 to /dev/null
/dev/null 是黑洞文件,所有写入它的内容都会永远丢失,而尝试从它那儿读取内容则什么也读不到
使得守护进程不与终端设备相关联,所以其输出无处显示,也无处从交互式用户那里接受输入
*/
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
/*
Initialize the log file.
*/
openlog(cmd,LOG_CONS,LOG_DAEMON);
if(fd0!=0 ||fd1!=1 ||fd2!=2){
syslog(LOG_ERR,"unexpected file descritors %d %d %d\n",fd0,fd1,fd2);
exit(1);
}
printf("finish daemonize..\n");
}
int main(void){
deamonize("daemon");
sigset_t waitmask;
sigemptyset(&waitmask);
sigaddset(&waitmask,SIGUSR1);
if(sigsuspend(&waitmask)!=-1)
printf("sigsuspend err\n");
//sigsuspend(&waitmask);
}
守护进程的出错记录:syslog()
3种产生日志消息的方法:
syslogd守护进程读取3种格式的日志消息
单例 守护进程--》使用文件和记录锁来保证一个守护进程只有一个副本在运行
/*
守护进程 -- 出错记录
守护进程没有控制终端,所以不能只是简单地写道标准错误上
--》需要有一个集中的守护进程出错记录措施--》syslog
单例 守护进程--》使用 文件和记录锁 来保证一个守护进程只有一个副本在运行
通过文件锁避免重复运行多个守护进程 --> lockf()
output: 查看 DeamonSysLog_13_6守护进程
kali@kali:~/Desktop/LinuxStudy/Hellos/Chapter13$ sudo cat /var/log/syslog
Jun 17 21:43:11 kali Daemon test.: Daemon process is already running[Resource temporarily unavailable].
kali@kali:~/Desktop/LinuxStudy/Hellos/Chapter13$ pstree -pul
systemd(1)─┬─DaemonSysLog_13(9895,kali)
├─Daemon_13_1(7014,kali)
├─ModemManager(613)─┬─{ModemManager}(623)
│ └─{ModemManager}(626)
├─NetworkManager(577)─┬─{NetworkManager}(615)
│ └─{NetworkManager}(616)
*/
#include "apue.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOCKFILE "/home/kali/Desktop/LinuxStudy/Hellos/Chapter13/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
//#define MAXLINE 1024
#define PRINT_PID() printf( "Row[%d]: getpid=[%d].\n", __LINE__, getpid() )
/**
* Print a message and return to caller.
* Caller specifies "errnoflag".
*/
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
char buf[MAXLINE];
vsnprintf(buf, MAXLINE, fmt, ap);
if (errnoflag)
snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
strerror(error));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(NULL); /* flushes all stdio output streams */
}
void err_quit(const char*fmt,...){
va_list ap;
va_start(ap,fmt);
err_doit(0,0,fmt,ap);
va_end(ap);
exit(1);
}
extern int lockfile(int);
int already_running(void){
int fd;
char buf[16];
fd = open(LOCKFILE,O_RDWR|O_CREAT,LOCKMODE);
if(fd<0){
syslog(LOG_ERR,"cannot open %s:%s",LOCKFILE,strerror(errno));
exit(1);
}
}
void daemonize(const char*cmd){
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
printf("start daemonize..\n");
/*
Clear file creation mask,第一步:设置文件模式屏蔽字为0
*/
umask(0);
/*
Get maximum number of file descriptors
*/
if(getrlimit(RLIMIT_NOFILE,&rl)<0)
printf("%s: cannot get file limit",cmd);
/*
Become a session leader to lose controlling TTY
*/
if((pid = fork())<0)
printf("%s: can't fork\n",cmd);
else if(pid!=0)
exit(0);
setsid();/* Create a new session with the calling process as its leader. */
/*
Ensure future opens won't allocate controlling TTYs.
*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);/* 解除所有信号屏蔽 */
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL)<0){
printf("%s: can't ignore SIGHUP\n",cmd);
}
if((pid = fork())<0)
printf("%s: cannot fork",cmd);
else if(pid!=0)
exit(0);
PRINT_PID();
/*
Change the current working directory to the root so
we won't prevent file systems from being unmounted
*/
if(chdir("/")<0)
printf("%s: cannot change directory to /",cmd);
/*
Close all file descriptor,
使得守护进程不再持有从其父进程继承来的任何文件描述符
*/
if(rl.rlim_max == RLIM_INFINITY){
rl.rlim_max = 1024;
}
for (int i = 0; i < rl.rlim_max; i++)
{
close(i);
}
/*
Attach file descriptors 0,1,2 to /dev/null
/dev/null 是黑洞文件,所有写入它的内容都会永远丢失,而尝试从它那儿读取内容则什么也读不到
使得守护进程不与终端设备相关联,所以其输出无处显示,也无处从交互式用户那里接受输入
*/
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
/*
Initialize the log file.
*/
openlog(cmd,LOG_CONS,LOG_DAEMON);//LOG_CONS--log on the console if errors in sending
if(fd0!=0 ||fd1!=1 ||fd2!=2){
syslog(LOG_ERR,"unexpected file descritors %d %d %d\n",fd0,fd1,fd2);
exit(1);
}
/**
* 第七步:通过文件锁避免重复运行多个守护进程
*/
int lockfd = open( LOCKFILE, O_RDWR );
if( lockfd < 0 )
{
syslog( LOG_ERR, "Cannot lock file[%s], aborting[%s].\n", LOCKFILE, strerror(errno) );
//下面这一行无法打印到控制台,项目上应该打印到日志文件中
printf( "Cannot lock file[%s], aborting[%s].\n", LOCKFILE, strerror(errno) );
exit(-1);
}
if( lockf(lockfd,F_TLOCK,0) < 0 ) // lockf()--> apply, test or remove a POSIX lock on an open file
{
syslog( LOG_ERR, "Daemon process is already running[%s].\n", strerror(errno) );
//下面这一行无法打印到控制台,项目上应该打印到日志文件中
printf( "Daemon process is already running[%s].\n", strerror(errno) );
exit(-2);
}
printf("finish daemonize..\n");
}
int main( int argc, char **argv )
{
PRINT_PID();
daemonize( "Daemon test." );
//由于父进程退出,所有只有最后一个子进程执行下面的语句(休眠)
printf( "This line will not be print for the daemon process has no terminate.\n" );
while(1)
sleep(120);
return 0;
}