Linux信号处理和守护进程

一、信号的概念
- 信号随时发生,接受信号的进程也可以没有控制权
- 每个信号名都以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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少言才不会咸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值