守护进程、系统日志、软中断信号
守护进程
1. daemon进程
- Unix/Linux中的
守护进程(Daemon)
类似于Windows中的后台服务进程,一直在后台长时间运行的进程。 守护进程
通常在系统启动后就运行,没有控制终端,也无法和前台的用户交互,在系统关闭时才结束。Daemon程序
一般都作为服务程序使用,等待客户端程序与它通信。我们也把运行的Daemon程序称作守护进程。- daemon进程一旦启动就能在后台一直运行,不会随着terminal的退出而结束。
- 守护进程需要使用ps aux命令查出进程ID然后再使用kill命令停止。
daemon()库函数
函数原型:
#include <unistd.h>
int daemon(int nochdir, int noclose);
函数功能:
- 创建daemon进程。
参数说明:
- nochdir:指定是否要切换当前工作路径到
/
根目录, - noclose:指定是否要关闭标准输入、标准输出和标准出错(即重定向到/dev/null)。
- 在创建守护进程的时候,往往需要将进程的工作目录修改为"/"根目录,并将标准输入、标准输出和标准出错关闭。所以这两个参数我们一般都是传0。
系统日志
- syslog是一种工业标准的协议,可用来记录设备的日志。
- 在UNIX系统,路由器、交换机等网络设备中,系统日志(System Log)记录系统中任何时间发生的大小事件。管理者可以通过查看系统记录,随时掌握系统状况。
- UNIX的系统日志是通过syslogd这个进程记录系统有关事件记录,也可以记录应用程序运作事件。
- 通过适当的配置,可以实现运行syslog协议的机器间通信,通过分析这些网络行为日志,追踪掌握与设备和网络有关的状况。
Linux系统自带的日志系统 syslog 函数
openlog()
函数原型:
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
函数功能:
- 打开日志设备,以供读取和写入,与文件系统调用的open类似;调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。
参数说明:
- ident:是一个标记,ident 所表示的字符串将固定的加在每行日志的前面来标识这个日志,通常就写成当前程序的名称以作标记。
- option: 指定openlog函数和接下来调用的syslog函数的控制标志。可以取以下值:
option | 说明 |
---|---|
LOG_CONS | 如果将信息发送给 syslogd 守护进程时发生错误,直接将相关信息输出到终端 |
LOG_NDELAY | 立即打开与系统日志的连接(通常情况下,只有在产生第一条日志信息的情况下才会打开与日志系统的连接) |
LOG_ODELAY | 类似于 LOG_NDELAY 参数,与系统日志的连接只有在 syslog 函数调用时才会创建 |
LOG_PERROR | 在将信息写入日志的同时,将信息发送到标准错误输出 |
LOG_PID | 每条日志信息中都包含进程号 |
- facility:指定记录消息程序的类型,与 syslogd 守护进程的配置文件 syslog.conf 中的 facility 对应。可取如下值:
facility | 说明 |
---|---|
LOG_AUTH | 认证系统(login、su、getty等) |
LOG_AUTHPRIV | 同 LOG_AUTH 但只登陆到所选择的单个用户可读的文件中。 |
LOG_CRON | cron 守护进程 |
LOG_DAEMON | 其他系统守护进程,如 routed |
LOG_FTP | 文件传输协议:ftpd、tftpd |
LOG_KERN | 内核产生的消息 |
LOG_LPR | 系统打印机缓冲池:lpr、lpd |
LOG_MAIL | 电子邮件系统 |
LOG_NEWS | 网络新闻系统 |
LOG_SYSLOG | 由 syslogd(8)产生的内部消息 |
LOG_USER | 随机用户进程产生的消息 |
LOG_UUCP UUCP | 子系统 |
LOG_LOCAL0 ~ LOG_LOCAL7 | 本地使用保留 |
syslog()
函数原型:
void syslog(int priority, const char *format, …);
函数说明:
- 写入日志,与文件系统调用 printf使用方法类似,但在前面指定日志级别。
参数说明:
- priority:表示消息的级别,与 syslogd 守护进程的配置文件syslog.conf 中的 level 对应。可取如下值:
priority | 说明 |
---|---|
LOG_EMERG | 紧急情况 |
LOG_ALERT | 应该被立即改正的问题,如系统数据库破坏 |
LOG_CRIT | 重要情况,如硬盘错误 |
LOG_ERR | 错误 |
LOG_WARNING | 警告信息 |
LOG_NOTICE | 不是错误情况,但是可能需要处理 |
LOG_INFO | 情报错误 |
LOG_DEBUG | 包含情报的信息,通常指在调试一个程序时使用 |
closelog()
函数原型:
void closelog(void);
函数说明:
- 关闭日志设备,与文件系统调用的close类似;调用closelog也是可选择的,它只是关闭被用于与syslog守护进程通信的描述符。
软中断信号
软中断信号
(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。异步事件
:不知道事件什么时候发生。- 信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
- 进程之间可以互相通过系统调用kill()发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
- 信号机制除了基本通知功能外,还可以传递附加信息。 收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
- 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
- 第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
- 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
kill -l 命令
:查看当前系统支持的信号,不同的系统支持的信号是不一样的。- 信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。
信号编号 | 信号名 | 信号说明 | 默认动作 |
---|---|---|---|
2 | SIGINT | Ctrl+C按键终止程序运行的信号 | 程序终止 |
4 | SIGILL | 非法的指令 | 程序终止 |
7 | SIGBUS | 运行非本CPU相关编译器编译的程序 | 程序终止 |
9 | SIGKILL | 强制杀死程序信号,任何程序都不可以捕捉该信号 | 程序终止,不可被捕捉 |
10 | SIGUSR1 | 用户自定义信号1 | 程序终止 |
11 | SIGSEGV | 段错误系统给程序发送的信号 | 程序终止 |
12 | SIGUSR2 | 用户自定义信号2 | 程序终止 |
13 | SIGPIPE | 管道破裂信号 | 程序终止 |
14 | SIGALRM | alarm()系统调用发送的信号 | 程序终止 |
15 | SIGTERM | kill命令默认发送的信号,默认动作是终止信号 | 程序终止 |
17 | SIGCHLD | 子进程退出信号 | 忽略该信号 |
signal()库函数
- 不安全,可能导致信号丢失。
函数原型:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
代码演示:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int g_stop = 0;
void sig_handle(int signum)
{
printf("catch signal [%d]\n",signum);
g_stop = 1;
}
int main(void)
{
signal(SIGINT,sig_handle);
signal(SIGTERM,sig_handle);
while( !g_stop )
{
;
}
printf("power off gprs\n");
}
- 执行代码后,用ctrl+c关闭进程,结果如下:
^Ccatch signal [2]
power off gprs
- 执行代码后,用kill关闭进程,结果如下:
catch signal [15]
power off gprs
sigaction()库函数
- 安全
代码演示:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <execinfo.h>
int g_sigstop = 0;
void signal_stop(int signum)
{
if( SIGTERM == signum )
{
printf("SIGTERM signal detected\n");
}
else if( SIGALRM == signum )
{
printf("SIGALRM signal detected\n");
g_sigstop = 1;
}
}
void signal_user(int signum)
{
if(SIGUSR1 == signum)
{
printf("SIGUSR1 signal detected\n");
}
else if(SIGUSR2 == signum)
{
printf("SIGUSR2 signal detected\n");
}
g_sigstop = 1;
}
void signal_code(int signum)
{
if(SIGBUS == signum)
{
printf("SIGBUS signal detected\n");
}
else if(SIGILL == signum)
{
printf("SIGILL signal detected\n");
}
else if(SIGSEGV == signum)
{
printf("SIGSEGV signal detected\n");
}
exit(-1);
}
int main(int argc, char **argv)
{
char *ptr=NULL;
struct sigaction sigact, sigign;
signal(SIGTERM, signal_stop);
signal(SIGALRM, signal_stop);
signal(SIGBUS, signal_code);
signal(SIGILL, signal_code);
signal(SIGSEGV, signal_code);
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = signal_user;
sigaction(SIGUSR1, &sigact, 0); /* catch SIGUSR1 */
sigaction(SIGUSR2, &sigact, 0); /* catch SIGUSR1 */
sigemptyset(&sigign.sa_mask);
sigign.sa_flags = 0;
sigign.sa_handler = SIG_IGN;
sigaction(SIGINT, &sigign, 0); /* ignore SIGINT signal by CTRL+C */
printf("Program start running for 20 seconds...\n");
alarm(20);
while( !g_sigstop )
{
;
}
printf("Program start stop running...\n");
printf("Invalid pointer operator will raise SIGSEGV signal\n");
*ptr = 'h';
return 0;
}