进程 1 int main (int argc, char *argv[]) ;
当内核执行c程序时(使用一个exec函数),在调用main前先调用一个特殊的启动例程。可执行文件将此启动例程指定为程序的起始地址。启动例程从内核取得命令行参数和环境变量的值。然后为调用main函数做好准备。
进程终止有八种方式,其中五种为正常终止:
正常终止
从main返回
调用exit
调用_exit或_Exit
最后一个线程从其启动例程返回
最后一个线程调用pthread_exit
异常终止:
调用abort
接到一个信号
最后一个线程对取消请求作出响应
1 2 3 4 5 #include <stdlib.h> void exit (int status) ;void _Exit(int status);#include <unsitd.h> void _exit(int status);
这三个函数用于正常终止一个程序:_exit和_Exit立即进入内核,exit先执行一些清理处理,然后返回内核。
exit函数总是执行一个标准I/O库的清理关闭操作(对所有打开流调用fclose函数)。通过atexit函数向exit登记终止处理程序 。exit调用这些函数的顺序与它们登记的顺序相反。
1 2 #include <stdlib.h> int atexit (void (*func)(void )) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static void my_exit1 (void ) ;static void my_exit2 (void ) ;int main (void ) { if (atexit(my_exit2) != 0 ) err_sys("can't register my_exit2" ); if (atexit(my_exit1) != 0 ) err_sys("can't register my_exit1" ); if (atexit(my_exit1) != 0 ) err_sys("can't register my_exit1" ); printf ("main is done\n" ); return (0 ); } static void my_exit1 (void ) { printf ("first exit handler\n" ); } static void my_exit2 (void ) { printf ("second exit handler\n" ); } output: main is done first exit handler first exit handler second exit handler
内核使程序执行的唯一方法是调用execl函数,进程自愿终止的唯一方法是显示或隐式地(exit)调用_exit和_Exit。
命令行参数的argv[0]为程序名。
1 2 3 4 5 6 7 8 int main (int argc, char *argv[]) { int i; for (i = 0 ; i < argc; i++) printf ("argv[%d]: %s\n" , i, argv[i]); exit (0 ); }
每个程序都接收到一张环境表,环境表也是一个字符指针数组,每个指针包含一个以null结束的C字符串的地址。
全局变量environ包含了该指针数组的地址
每个字符串的结尾处都显式有一个null字节
我们称environ为环境指针,指针数组为环境表,其中各指针指向的字符串为环境字符串
使用getenv和putenv函数来访问特定的环境变量。
c程序内存布局 历史沿袭至今,c成员有以下几部分组成:
正文段(txt)。正文段通常是可共享,只读 。
初始化数据段。例如任何之外的函数声明。int maxcount = 99
未初始化数据段(bss)。long sum[1000]
栈(stack)。
堆(heap)。
未初始化数据段 (bss)的内容不存放在磁盘上,内核在程序开始运行前将它们设置为0,需要存放在磁盘程序文件中的段只有正文段和初始化数据段。
size命令可查看各段长度(以字节为单位)。
存储空间分配 1 2 3 4 5 6 7 #include <stdlib.h> void *malloc (size_t size) ; void *calloc (size_t nobj, size_t size) ; void *realloc (void *ptr, size_t newsize) ; void free (void *ptr) ;
环境变量 1 2 3 4 5 6 #include <stdlib.h> char *getenv (const char *name) ;int putenv (char *str) ;int setenv (const char *name, const char *value, int rewrite) ;int unsetenv (const char *name) ;
c语言中的goto语句是不能跨越函数的,setjmp和longjmp可以实现跨越函数的跳转。
非局部goto setjmp和longjmp,非局部指的是这不是由普通的c语言goto语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中去。
1 2 3 4 #include <setjmp.h> int setjmp (jmp_buf env) ;void longjmp (jmp_buf, int val) ;
每个进程都有一组资源限制,其中一些可以用getrlimit和setrlimt查询和修改。
1 2 3 4 5 6 7 #include <sys/resource.h> struct rlimit { rlim_t rlim_cur; rlim_t rlim_max; }; int getrlimit (int resource, struct rlimit *rlptr) ;int setrlimit (int resource, const struct rlimit *rlptr) ;
进程控制 进程标识 每个进程都有一个非负整型表示的唯一进程ID。进程id是可复用的,复用使用延迟复用算法,防止新进程是原来的进程。
ID为0的进程通常是调度进程,常被称为交换进程(swapper)。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。
进程ID为1的通常是init进程,在自举过程结束时由内核调用。此进程负责在自觉内核后启动一个unix系统。init通常读取与系统有关的初始化文件(/etc/rc*文件或/etc/inittab文件或在/etc/init.d中的文件),init进程不会终止,它是一个普通的用户进程,以超级用户特权运行。
某些unix的虚拟存储器实现中,进程ID2是页守护进程(page daemon),此进程负责支持虚拟存储器系统的分页操作。
1 2 3 4 5 6 7 8 #include <unistd.h> pid_t getpid (void ) ; pid_t getppid (void ) ; uid_t getuid (void ) ; uid_t geteuid (void ) ; gid_t getgid (void ) ; gid_t getegid (void ) ;
fork函数 一个现有的进程可以调用fork函数创建一个新进程。创建的新进程为子进程,fork函数执行一次,但返回两次。子进程返回值为0,父进程返回子进程的进程ID。
1 2 #include <unistd.h> pid_t fork (void ) ;
fork后的子进程获得父进程数据空间,堆和栈的副本 (不共享)。这些副本不是立即分配,而是采用了一种写时复制 (Copy-On-Write, COW)技术,内核将父子进程共享的部分设置为只读,当有进程试图修改这些区域时,内核为要修改的区域制作一个副本,通常是虚拟存储系统中的一页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "apue.h" int glob = 6 ; char buf[] = "a write to stdout\n" ;int main (void ) { int var; pid_t pid; var = 88 ; if (write(STDOUT_FILENO, buf, sizeof (buf)-1 ) != sizeof (buf)-1 ) err_sys("write error" ); printf ("before fork\n" ); if ((pid = fork()) < 0 ) { err_sys("fork error" ); } else if (pid == 0 ) { glob++; var++; } else { sleep(2 ); } printf ("pid = %d, glob = %d, var = %d\n" , getpid(), glob, var); exit (0 ); }
fork一个子进程会将父进程的所有打开文件描述符都复制到子进程中。父进程与子进程每个相同的文件描述符共享一个文件表项。父子进程还共享同一个文件偏移量 。
exit函数 除打开文件外,子进程还继承了一些父进程的属性:
实际用户id,实际组id,有效用户id,有效组id。
附属组id。
进程组id。
会话id。
控制终端。
设置用户id标志和设置组id标志。
当前工作目录。
根目录。
文件模式创建屏蔽字。
信号屏蔽和安排。
对任一打开文件描述符的执行时关闭标志。
环境。
链接的共享内存。
存储映像。
资源限制。
一些区别:
fork的返回值不同。
进程ID不变。
子进程的tms_utime,tms_stime,tms_cutime和tms_ustime的值设置为0。
子进程不继承父进程设置的文件锁。
子进程的未处理闹钟被清除。
子进程的未处理信号集设置为空集。
不管进程如何终止,最后都会执行内核中的同一段代码,为相应进程关闭所有打开描述符,释放它所使用的存储器等。
孤儿进程 :如果父进程在子进程之前终止,它们的父进程到改变为init进程。
僵尸进程 :如果子进程在父进程之前终止,但是其父进程尚未进行善后处理(终止子进程的相关信息,释放它占用的资源)就会成为(zombie)僵尸进程。
wait和waitpid函数 当一个进程正常或者异常终止时,内核就向其父进程发送SIGCHLD信号。
调用wait或waitpid会发生如下情况:
如果其所有子进程都还在运行,则阻塞。
如果一个子进程已经终止,正等待父进程获取其终止状态,则取得改子进程的终止状态后马上返回。
如果没有任何子进程,则立即出错返回-1。
1 2 3 4 #include <sys/wait.h> pid_t wait (int *statloc) ;pid_t waitpid (pid_t pid, int *statloc, int options) ;
statloc是一个整形指针,如果statloc不是一个控指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可以将参数指定未空指针。
这两个函数返回的整型状态字是由实现宏定义的。其终止状态可在如下宏来查看:
宏
说明
WIFEXITED(status)
若为正常终止紫禁城返回的状态则为真
WIFSIGNALED(status)
若为异常终止紫禁城返回的状态则为真
WIFSTOPPED(status)
若为当前暂停子进程的返回状态则为真
WIFCONTINUED(status)
若在作业控制暂停后已经继续的子进程返回状态则为真
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include "apue.h" #include <sys/wait.h> void pr_exit (int status) { if (WIFEXITED(status)) printf ("normal termination, exit status = %d\n" , WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf ("abnormal termination, signal number = %d%s\n" , WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? " (core file generated)" : "" ); #else "" ); #endif else if (WIFSTOPPED(status)) printf ("child stopped, signal number = %d\n" ,WSTOPSIG(status)); }
waitpid函数提供了wait函数没有提供的3个功能:
waitpid可等待一个特定的进程,而wait则返回任意终止子进程的状态。
waitpid提供了一个wait的非阻塞版本。有时希望获取一个子进程的状态,但不想阻塞。
waitpid通过WUNTRACED和WCONTINUED选项支持作业控制。(options参数提供)
pid参数的作用解释如下:
pid==-1 等待任意子进程。
pid>0 等待进程id和pid相等的子进程。
pid==0 等待组id等于调用组id的任一子进程。
pid<-1 等待组id等于pid绝对值的任一子进程。
其它wait类型函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <sys/wait.h> int waitid (idtype_t idtype, id_t id, siginfo_t *infop, int options) ; #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3 (int *statloc, int options, struct rusage *rusage) ; pid_t wait4 (pid_t pid, int *statloc, int options, struct rusage *rusage) ;
当多个进程企图对共享数据进行某种处理,而最后的结果取决于进程运行的顺序时,我们认为发生了竞争条件 。
exec函数 当使用fork创建新的子进程后,子进程往往需要调用一种exec函数以执行另一个程序。当进程调用一种exec函数的时候,该进程执行的程序完全替换未新程序,新程序从自己的main函数开始执行。因为调用exec并不创建新进程,所以前后的进程id并不改变。exec只是使用磁盘上的一个新程序替换了当前进程的正文段,数据段,堆段和栈段。
进程基本控制原语:使用fork创建新进程,用exec执行新的程序,exit和wait函数处理终止和等待终止。
1 2 3 4 5 6 7 8 9 10 11 #include <unistd.h> int execl (const char * pathname, const char *arg0, ...) ;int execv (const char *pathname, char *const argv[]) ;int execle (const char *pathname, const char *arg0) ;int execve (const char *pathname, char *const argv[],char *const envp[]) ;int execlp (const char *filename, char *arg0, ...) ;int execvp (const char *filename, char *const argv[]) ;int fexecve (int fd, char *const argv[], char *const envp[]) ;
当使用filenname做参数的时候,如果filename包含/,则将其视为路径名,否则就按PATH环境变量,在它所指定的目录中搜索可执行文件。
如果execlp或execvp使用路径前缀中的一个找到了一个可执行文件,但是该文件不是由连接编辑器产生的机器可执行文件,则就认为该文件是一个shell脚本,于是试着调用sh并以该filename作为输入。
在执行exec后,进程ID没有改变,但新程序从调用进程继承了下列属性:
进程ID和父进程ID
实际用户ID和实际组ID
附属组ID
进程组ID
会话ID
控制终端
闹钟尚余留的时间
当前工作目录
根目录
文件模式创建屏蔽字
文件锁
进程信号屏蔽
未处理信号
资源限制
nice值
tms_utime,tms_stime,tms_cutime以及tms_cstime值
对于打开文件的处理与每个描述符的执行时(close-on-exec)标志有关(FD_CLOEXEC)标志。进程中每个打开描述符都有一个执行时关闭的标志。若设置了此标志,则在执行exec时关闭该描述符;否则该描述符仍打开。除非特地用fcntl设置了该执行时关闭标志,否则系统的默认操作是exec后仍保持这种描述符打开。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include "apue.h" #include <sys/wait.h> char *env_init[] = { "USER=unknown" , "PATH=/tmp" , NULL };int main (void ) { pid_t pid; if ((pid = fork()) < 0 ) { err_sys("fork error" ); } else if (pid == 0 ) { if (execle("/home/sar/bin/echoall" , "echoall" , "myarg1" , "MY ARG2" , (char *)0 , env_init) < 0 ) err_sys("execle error" ); } if (waitpid(pid, NULL , 0 ) < 0 ) err_sys("wait error" ); if ((pid = fork()) < 0 ) { err_sys("fork error" ); } else if (pid == 0 ) { if (execlp("echoall" , "echoall" , "only 1 arg" , (char *)0 ) < 0 ) err_sys("execlp error" ); } exit (0 ); }
更改用户ID和更改组ID 1 2 3 4 5 6 #include <unistd.h> int setuid (uid_t uid) ; int setgid (gid_t gid) ; int seteuid (uid_t uid) ; int setegid (gid_t uid) ;
若进程具有超级用户特权,则setuid函数将实际用户ID,有效用户ID以及保存的设置用户ID设置为uid。
若进程没有超级用户特权,但是uid等于实际用户ID或保存的设置用户ID,则setuid只将有效用户ID设置为uid。不更改实际用户ID和保存的设置用户ID。
如果上述条件都不满足,则errno设置为EPERM,并返回-1。
system函数 1 2 #include <stdlib.h> int system (const char *cmdstring) ;
fork失败或者waitpid返回除EINTR之外的出错,则system返回-1,并且设置errno以指示错误类型。
如果exec失败(表示不能执行shell),则其返回值如果shell执行了exit(127)一样。
否则所有3个函数(fork,exec和waitpid)都成功,那么system的返回值是shell的终止状态。
进程调度 进程可以通过调整nice值选择以更低优先级运行。只有特权进程允许提高调度权限。nice值越低,优先级越高。进程只能影响自己的nice值,不能影响任何其它进程的nice值。
1 2 #include <unistd.h> int nice (int incr) ;
incr参数被增加到调用进程的nice值上。如果incr太大,系统直接把它降到最大合法值,不给提示。
getpriority函数可以像nice函数那样用于获取进程的nice值,但是getpriority还可以获取一组相关进程的nice值。成功返回nice值,失败返回-1。
1 2 #include <sys/resource.h> int getpriority (int which, id_t who) ;
参数which取值:
PRIO_PROCESS表示进程
PRIO_PGRP表示进程组
PRIO_USER表示用户ID
与getprioritywhich取值一样,设置nice值。
1 2 #include <sys/resource.h> int setpriority (int which, id_t who, int value) ;
1 2 3 4 5 6 7 8 9 10 #include <sys/times.h> struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; } clock_t times (struct tms *buf) ;
进程关系 登录 终端登录 网络登录 进程组 进程组 是一个或多个进程的集合。通常它们是在一个作业中被组合起来的。
同一个进程组中的各进程接收来自同一个终端的各种信号。
每个进程组有一个唯一的进程组ID(组长进程), 组长进程的进程组id等于其进程id。
1 2 3 #include <unistd.h> pid_t getpgrp (void ) ;pid_t getpgid (pid_t pid) ;
getpgrp返回调用进程的进程组id,getpgid返回参数pid进程的进程组id。当pid是0时,返回调用进程的进程组id。getpgid(0) == getpgrp()
进程组组长可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程中有一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。
进程调用setpgid函数可以加入一个现有的进程组或创建一个新进程组。setpgid函数将pid进程的进程组id设置为pgid。如果两个参数相等,则由pid指定的进程变成进程组组长。如果pid是0,则使用调用者的进程id。如果pgid是0,则由pid指定的进程id用作进程组id。
1 2 #include <unistd.h> int setpgid (pid_t pid, pid_t pgid) ;
一个进程只能为它自或它的子进程设置组id,在他的子进程调用了exec后,他就不在更改该子进程的进程组id
会话
会话 (session)是一个多个或多个进程组的集合,通常是由shell管道讲几个进程编程一组的。
进程调用setsid(void)创建一个新会话。
1 2 3 #include <unistd.h> pid_t setsid (void ) ;pid_t getsid (pid_t pid) ;
如果调用此函数不是一个进程组的组长,则此函数创建一个新会话。
该进程变成新会话的会话首进程(创建该会话的进程),此时,该进程是新会话中的唯一进程
该进程成为一个新进程的组长进程,新进程组id是该调用进程的进程ID
该进程没有控制终端
控制终端
一个会话可以有一个控制终端 (controlling terminal)
建立与控制终端连接的会话首进程被称为控制进程 (controlling process)
一个会话中的几个进程组可被分成一个前台进程组 和一个后台进程组
无论何时键入终端的中断键 (DELETE或Ctrl+C),都会将中断信号发送至前台进程组的所有进程
无论何时键入终端的退出键 (Ctrl+),都会将退出信号发送至前台进程组的所有进程
如果终端接口检测到终端断开链接,则将挂断信号发送至终端控制进程
守护进程 守护进程 (daemon)是生存期长的一种进程。
首先要做的是调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)。继承而来的文件模式创建屏蔽字可能会被设置为拒绝某些权限,如果守护进程要创建文件,那么可能要设置特点的权限。
调用fork,然后使父进程exit。
调用setsid创建一个新会话。
将当前工作目录更改为根目录。
关闭不再需要的文件描述符。
某些守护进程打开/dev/null使其具有文件描述符0,1,2。这样任何一个试图读标准输入,写标准输出或标准错误的库例程 都不会产生任何效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include "apue.h" #include <syslog.h> #include <fcntl.h> #include <sys/resource.h> void daemonize (const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl ; struct sigaction sa ; umask(0 ); if (getrlimit(RLIMIT_NOFILE, &rl) < 0 ) err_quit("%s: can't get file limit" , cmd); if ((pid = fork()) < 0 ) err_quit("%s: can't fork" , cmd); else if (pid != 0 ) exit (0 ); setsid(); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) err_quit("%s: can't ignore SIGHUP" ); if ((pid = fork()) < 0 ) err_quit("%s: can't fork" , cmd); else if (pid != 0 ) exit (0 ); if (chdir("/" ) < 0 ) err_quit("%s: can't change directory to /" ); if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024 ; for (i = 0 ; i < rl.rlim_max; i++) close(i); fd0 = open("/dev/null" , O_RDWR); fd1 = dup(0 ); fd2 = dup(0 ); openlog(cmd, LOG_CONS, LOG_DAEMON); if (fd0 != 0 || fd1 != 1 || fd2 != 2 ) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d" , fd0, fd1, fd2); exit (1 ); } }
syslog出错记录
syslogd守护进程读取3种格式的日志消息:
内核例程可以调用log函数
大多数用户进程(守护进程)调用syslog函数产生日志消息。
日志消息发向UDP514端口。
syslogd守护进程在启动时读取/etc/syslog.conf配置文件。
1 2 3 4 5 #include <syslog.h> void openlog (const char *ident, int option, int facility) ;void syslog (int priority, const char *format, ...) ;void closelog (void ) ;int setlogmask (int maskpri) ;
openlog函数是可选的,如果不调用openlog,则在第一次调用syslog时,自动调用openlog。closelog函数也是可选的。
ident参数可以被加载至每条日志消息中。一般是程序的名称。
setlogmask函数用于设置进程的记录优先级屏蔽字。
level
说明
LOG_EMERG
紧急(系统不可使用)(最高优先级)
LOG_ALERT
必须立即修复的情况
LOG_CRIT
严重情况
LOG_ERR
出错情况
LOG_WARNING
警告情况
LOG_NOTICE
正常但重要的情况
LOG_INFO
信息性消息
LOG_DEBUG
调试消息(最低优先级)
单实例守护进程 某些守护进程在任一时刻只运行该守护进程的一个副本。文件和记录锁 机制为一种方便的互斥机制,该方法保证了一个守护进程只有一个副本在运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <syslog.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 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, "can't open %s: %s" , LOCKFILE, strerror(errno)); exit (1 ); } if (lockfile(fd) < 0 ) { if (errno == EACCES || errno == EAGAIN) { close(fd); return (1 ); } syslog(LOG_ERR, "can't lock %s: %s" , LOCKFILE, strerror(errno)); exit (1 ); } ftruncate(fd, 0 ); sprintf (buf, "%ld" , (long )getpid()); write(fd, buf, strlen (buf)+1 ); return (0 ); }
若守护进程使用锁文件,那么该文件通常存储在/var/run目录中。
若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。
守护进程可用命令行启动,但通常它们是由系统初始化脚本之一。/etc/rc*或/etc/init.d/*
某些守护进程捕捉SIGHUP信号,当接收到该信号时,重新读配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include "apue.h" #include <pthread.h> #include <syslog.h> sigset_t mask;extern int already_running (void ) ;void reread (void ) { } void *thr_fn (void *arg) { int err, signo; for (;;) { err = sigwait(&mask, &signo); if (err != 0 ) { syslog(LOG_ERR, "sigwait failed" ); exit (1 ); } switch (signo) { case SIGHUP: syslog(LOG_INFO, "Re-reading configuration file" ); reread(); break ; case SIGTERM: syslog(LOG_INFO, "got SIGTERM; exiting" ); exit (0 ); default : syslog(LOG_INFO, "unexpected signal %d\n" , signo); } } return (0 ); } int main (int argc, char *argv[]) { int err; pthread_t tid; char *cmd; struct sigaction sa ; if ((cmd = strrchr (argv[0 ], '/' )) == NULL ) cmd = argv[0 ]; else cmd++; daemonize(cmd); if (already_running()) { syslog(LOG_ERR, "daemon already running" ); exit (1 ); } sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) err_quit("%s: can't restore SIGHUP default" ); sigfillset(&mask); if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL )) != 0 ) err_exit(err, "SIG_BLOCK error" ); err = pthread_create(&tid, NULL , thr_fn, 0 ); if (err != 0 ) err_exit(err, "can't create thread" ); exit (0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include "apue.h" #include <syslog.h> #include <errno.h> extern int lockfile (int ) ;extern int already_running (void ) ;void reread (void ) { } void sigterm (int signo) { syslog(LOG_INFO, "got SIGTERM; exiting" ); exit (0 ); } void sighup (int signo) { syslog(LOG_INFO, "Re-reading configuration file" ); reread(); } int main (int argc, char *argv[]) { char *cmd; struct sigaction sa ; if ((cmd = strrchr (argv[0 ], '/' )) == NULL ) cmd = argv[0 ]; else cmd++; daemonize(cmd); if (already_running()) { syslog(LOG_ERR, "daemon already running" ); exit (1 ); } sa.sa_handler = sigterm; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGHUP); sa.sa_flags = 0 ; if (sigaction(SIGTERM, &sa, NULL ) < 0 ) { syslog(LOG_ERR, "can't catch SIGTERM: %s" , strerror(errno)); exit (1 ); } sa.sa_handler = sighup; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGTERM); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) { syslog(LOG_ERR, "can't catch SIGHUP: %s" , strerror(errno)); exit (1 ); } exit (0 ); }