您的位置:首页 > 运维架构 > Linux

Linux信号处理和守护进程

2016-11-25 18:59 399 查看
一、信号的概念
- 信号随时发生,接受信号的进程也可以没有控制权
- 每个信号名都以SIG开头,包含在<signal.h>中
- 当进程接收到一个信号,他可以对信号采取如下操作之一:
忽略这个信号
捕获这个信号,这需要执行一段称为信号处理器的特殊代码
允许执行信号的默认操作
- 当进程对发送给它的信号采取措施,叫信号被传送
- 产生信号和递送信号之间的时间间隔叫做信号为决
- 进程不能简单的通过判断一个变量,如errno来判断是否出现一个信号

二、捕获信号
- 当一个进程调用fork的时候,其子进程继承父进程的信号处理方式
所以信号捕捉函数的地址在子进程中式有意义的
- 利用函数指针回调函数捕获信号
- 进程捕捉到信号并对信号进行处理时,进程正在执行的指令序列临
时中断,它首先执行该信号处理程序中的指令
- 如果信号处理程序返回(没有调用exit(0)或者abort),则继续执行在
捕捉到信号时正在执行的正常指令序列
- 在信号处理程序中,不能判断捕捉到信号时进程正在何处执行

signal 函数(若为命令行,则使用kill向指定进程发送信号)
#include <signal.h>
void (*signal(int signo,void (*func)(int)))(int);
//参数signo是前面表格中的信号名
//参数func式接收到此信号后要调用的函数。该函数有个int
型参数,int代表捕获到的信号值

/*signal函数的例子*/
void catch_Signal(int Sign)
{
switch (Sign)
{
case SIGINT:
printf ("SIGINT Signal\n");
break;//改变了消息的用途,可以收到CTRL+C后退出进程
case SIGALRM:
printf ("HELLO\n");
exit(0);

}

}

int main()
{
signal(SIGINT,catch_Signal);
signal(SIGALRM,catch_Signal);
pause ();
return 0;
}

三、发送信号
1)kill
- 使用kill命令
- 使用kill函数

#include <signal.h>
#include <sys/types.h>
int kill(pid_t pid,int sig)
//参数pid指定一个要杀死的信号,而sig是要发送的信号

2)raise函数
#include <signal.h>
int raise(int signo);
//kill 函数将信号发送给进程,raise函数允许发信号给自身
//raise(signo)等价于kill(getpid(),signo);

3)alarm函数
- alarm函数设置一个定时器,当定时器到了就发送SIGALRM信号
#include <unistd.h>
unsigned  int alarm(unsigned  int seconds);
//seconds是计时器时间到后时钟的秒数
//如果没有设置其他超时,函数返回0,否则返回值为前面安排超时中保留的秒数
//一个进程只能设置一次超时
//把second设置为0可以取消前面的超时设置

四、改进捕获信号机制
sigaction函数的功能是检查或修改与指定信号相关联的
处理动作,该函数替代了signal函数
#include <studio.h>
int sigaction (int signo,const struct sigaction *act,
struct sigaction *oact);
//参数signo是要检测或者修改其具体动作的信号编号
(或同时执行这两种操作)
//如果act指针为空,则要修改其动作
//如果oact指针为空,则系统由oact指针返回该信号的上一个动作
//成功返回0,失败返回-1

struct sigaction{
void (*sa_handle)(int);
void (*sasigaction)(int,siginfo_t *,void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer )(void);
}

/*sigaction例子*/
void catch_Signal(int Sign)
{
switch (Sign)
{
case SIGINT:
printf ("SIGINT Signal\n");
exit(0);

}
}

int signal1(int signo,void (*func)(int))
{
struct sigaction act,oact;
act.sa_handler = func;//回调函数初始化
sigemptyset(&act.sa_mask);//初始化
act.sa_flags = 0;
return sigaction(signo,&act,&oact);

}

int main()
{
signal1(SIGINT,catch_Signal);
pause();
return 0;
}

五、使用自定义信号
/*自定义signal函数的例子*/
int static status = 0;

void catch_Signal(int Sign)
{
switch (Sign)
{
case SIGINT:
printf ("SIGINT Signal\n");
break;
case SIGUSR1:
status = 1;

}

}

int signal1(int signo,void (*func)(int))
{
struct sigaction act,oact;
act.sa_handler = func;//回调函数初始化
sigemptyset(&act.sa_mask);//初始化
act.sa_flags = 0;
return sigaction(signo,&act,&oact);

}
/*捕获SIGUSR1信号*/
int main()
{
signal(SIGINT,catch_Signal);
signal(SIGUSR1,catch_Signal);
printf("pid = %d\n",getpid());
while(1)
{
if(status = 1);
printf("SIGUSR1 UP!\n");
sleep(1);
}
return 0;
}

/*发送signal信号*/
int main(int argc,char *args[])
{
if(argc > 1)
{
kill(atoi(args[1],SIGUSR1);
printf("sent to %d\n",atoi(args[1]))
}

return 0;
}

六、守护进程(daemon)
- 守护进程是一个后台进程,无须用户输入就能运行
- 守护进程不能够控制终端,所以任何输入或者输出都需做特殊处理
- 守护进程执行fork之后就让父进程退出
- 子进程中调用setsid,取消进程和任何终端的关联
- 下一步是让根目录成为进程的当前工作目录(因为如果它的当前目录是在一个被安装的
文件系统上,那么就会妨碍这个文件系统被卸载)
- 接下来设置umask为0(为了避免守护进程集成的umask收到创建文件和目录操作的干扰,这
一步是必要的)
- 最后关闭子进程继承的任何不必要的文件描述符

总结:
1)父进程中执行fork,执行exit退出
2)在子进程中调用setid
3)让根目录'/'成为子进程的工作目录
4)把子进程的umask变为0
5)关闭不需要的文件描述符

setsid函数:pid_t setsid()
//setsid函数创建一个新会话和一个新进程组,然后守护进程成为新会话的领导
//以及新进程组的领导
//setsid还保证新会话没有控制终端
//如果调用进程已经是一个进程组的领导进程,setsid会失败
//setsid调用成功返回新会话id,失败返回-1,并设置errno

chdir函数:int chdir(const char*pathname)
//chdir函数根据参数pathname设置当前工作目录
//chdir函数调用成功返回0,失败返回-1,并设置errno

umask函数:mode_t umask(mode_t mask)
//umask调用把守护今晨的umask设置为0,这样取消了父进程的umask,避免了潜在的
//干扰创建文件和目录

/*创建守护进程代码*/
int main()
{
pid_t pid = fork();
if (pid == -1)
{
return -1;
}

if (pid > 0)
{
exit(0);
}
if(pid == 0)
{
setsid();//脱离控制台
chdir("/");//非必须,防止mount出错
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

}

while(1)
{
printf("hello\n");
sleep(1);
}

return EXIT_SUCCESS;

}

守护进程写入系统日志:
1)openlog函数打开日志:void openlog(const char *ident,int option,int facility);
//参数ident是要向每个消息加入的字符串,典型的情况是程序名称
//参数option是下面一个或多个值的"或"
名称                  含义
LOG_CONS            如果系统日志服务器不能用, 写入控制台
LOG_NDELAY          立即打开连接,正常情况下,直到发送第一条消息才打开连接
LOG_PERROR          打印输出到stderr
LOG_PID             每条消息中包含进程PID
//参数facitity指定程序发送消息的类型
名称                  含义
LOG_AUTHPRIV        安全授权信息
LOG_CRON            时钟守护进程,cron和at
LOG_DAEMON          其他系统守护进程
LOG_KERN            内核消息
LOG_LPR             打印机子系统
LOG_MAIL            邮件子系统
LOG_USER            默认
2)syslog写入日志:void syslog (int priority,const char * format,...)
//参数priority指定消息重要性
名称                  含义
LOG_EMERG           系统不能使用
LOG_ALERT           立即采取措施
LOG_CRIT            紧急事件
LOG_ERR             出错条件
LOG_WARNING         警告条件
LOG_NOTICE          正常但重大事件
LOG_INFO            信息消息
LOG_DEBUG           调试信息
3)closelog关闭日志:void closelog(void);

/*syslog日志例子*/
syslog(LOG_INFO,"my daemon is OK!");
/*严格说,openlog和closelog是可选的,因为函数syslog在首次使用的时候自动打开*/

使用信号与守护进程通信
void catch_Signal(int Sign)
{
switch(Sign)
{
case SIGTERM:
exit(EXIT_SUCCESS);
}
}

/*通过脚本结束守护程序*/
#!/bin/sh

WHOAMI=`whoami`

PID=`ps -u $WHOAMI | grep mydaemon | awk '{print} $1'`

if(test "$PID"!="") then
kill $PID
fi

/*通过脚本开启守护程序*/
#!/bin/sh

WHOAMI=`whoami`

PID=`ps -u $WHOAMI | grep abc | awk '{print} $1'`

if(test "$PID"="") then
./mydaemon
fi
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: