Linux C 信号系统

1 篇文章 0 订阅

ToDo

待整理信号屏蔽 和 未决信号。
当信号发生时, 信号处理函数在执行时,又有新的信号产生时,分两种情况:

相同信号:排队处理

不同信号:会中断当前信号处理,执行新的信号处理函数,处理完后再执行原来的信号处理函数

什么是信号?

是一种软中断,由内核传递到目标进程 或 通过系统调用(kill、raise)发送信号。

查看系统中所有信号

fzf@fzf:~/work/DailyTest/C$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

其中:

1 ~ 31叫非实时信号,可能会丢失,不支持排队

31往后的信号(到64):实时信号,支持排队,发送的多个实时信号都会被接收

ctrl + c:触发SIGINT

kill、killall:默认触发SIGTERM

ctrl + z:触发SIGTSTP

信号的几种处理方式

  1. 默认动作,大部分动作是中止进程;
  2. 忽略:指定后如果有信号发生,则进程忽略处理;
  3. 捕获:执行信号处理接口;

其中:SIGKILL和SIGSTOP信号不能被捕获、也不能被忽略。

注册信号处理函数

使用signal函数

#include <signal.h>

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
    
signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of  a  programmer-defined  function  (a "signal handler").

* If the signal signum is delivered to the process, then one of the following happens:

* If the disposition is set to SIG_IGN, then the signal is ignored.

* If the disposition is set to SIG_DFL, then the default action associated with the signal (see signal(7)) occurs.

*  If  the  disposition  is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum.  If invocation of the handler caused the signal to be blocked, then the signal  is  unblocked  upon return from the handler.

The signals SIGKILL and SIGSTOP cannot be caught or ignored.

RETURN VALUE
	signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error, errno is set to indicate the cause.
示例代码:
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

//定义信号处理函数, signo表示信号值
void sig_handler(int signo)
{
    printf("pid: %d, signal %d occured\n", getpid(), signo);
}

int main(void)
{
    /*
        向内核登记信号值以及信号处理函数
        忽略此信号时, 第二个参数传"SIG_IGN
    */
    if (signal(SIGTSTP, sig_handler) == SIG_ERR) { //ctrl + z
        perror("signal sigtstp error");
    }
    if (signal(SIGINT, sig_handler) == SIG_ERR) {  //ctrl + c
        perror("signal sigint error");
    }

    /*
        SIGKILL, SIGSTOP不能被忽略, 也不能被捕获
    */
    if (signal(SIGKILL, sig_handler) == SIG_ERR) {
        perror("signal sigkill error");
    }
    if (signal(SIGSTOP, sig_handler) == SIG_ERR) {
        perror("signal sigstop error");
    }

    if (signal(SIGKILL, SIG_IGN) == SIG_ERR) {
        perror("ingore signal sigkill error");
    }
    if (signal(SIGSTOP, SIG_IGN) == SIG_ERR) {
        perror("ignore signal sigstop error");
    }

    while (1) {
        sleep(5);
    }
    return 0;
}

使用sigaction函数

struct sigaction {
	void     (*sa_handler)(int); //函数指针, 信号处理函数
	void     (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask; //信号集,可以屏蔽信号
	int        sa_flags; //SA_RESTART
	void     (*sa_restorer)(void);
};
示例代码:
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>

static void sig_handler(int signo)
{
    printf("Received signal(%d)\n", signo);
}

int main(int argc, char *argv[])
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = sig_handler;
    int ret = sigaction(SIGINT, &sa, NULL);
    if (ret != 0) {
        perror("sigaction sigint failed.");
    }
    ret = sigaction(SIGKILL, &sa, NULL);
    if (ret != 0) {
        perror("sigaction sigkill failed.");
    }
    ret = sigaction(SIGSTOP, &sa, NULL);
    if (ret != 0) {
        perror("sigaction sigstop failed.");
    }

    sa.sa_handler = SIG_IGN;
    ret = sigaction(SIGKILL, &sa, NULL);
    if (ret != 0) {
        perror("sigaction sigkill failed.");
    }
    ret = sigaction(SIGSTOP, &sa, NULL);
    if (ret != 0) {
        perror("sigaction sigstop failed.");
    }

    while (1) {
        sleep(5);
    }

    return 0;
}

SIGCHLD信号(子进程退出产生该信号)

子进程结束时产生该信号,父进程需要使用wait调用来等待子进程结束并回收它

避免出现僵尸进程

示例代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

void sig_handler(int signo)
{
    printf("child process deaded, signo: %d\n", signo);
    //当父进程捕获到SIGCHLD信号后要调用wait函数回收子进程,否则子进程会成为僵尸进程
    wait(NULL);
}

void out(int num)
{
    int i = 0;
    while (i < num) {
        printf("pid: %d running %d\n", getpid(), i++);
        sleep(2);
    }
}

int main(void)
{
    if (signal(SIGCHLD, sig_handler) == SIG_ERR) {
        perror("signal sigchld error");
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) { //parent process
        out(100);
    } else { //child process
        out(10);
    }

    return 0;
}

信号发送

除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号

一般进程向相同uid 和 gid 的进程发送信号

kill函数

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

#返回值:returns 0 on success, and nonzero for failure

参数pid取值:

  • pid > 0:将信号发给进程ID为pid的进程
  • pid = 0:将信号发给与发送进程同一进程组的所有进程
  • pid < 0:将该信号发送给进程组ID等于pid的绝对值
  • pid = -1:将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程

当sig为0时,用来判断pid代表的进程是否存在

示例代码:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

//定义信号处理函数,signo表示信号值
void sig_handler(int signo)
{
    printf("pid: %d, signal %d occured\n", getpid(), signo);
}

int main(void)
{
    //向内核登记信号值以及信号处理函数
    if (signal(SIGINT, sig_handler) == SIG_ERR) {  //ctrl + c
        perror("signal sigint error");
    }

    int exist = kill(getpid(), 0); //判断该进程是否存在
    printf("process exist: %d\n", exist);

    int i = 0;
    while (i < 20) {
        printf("%d running %d\n", getpid(), i++);
        if (i == 10) {
            //kill(getpid(), SIGKILL); //把自己杀死
            kill(getpid(), SIGINT);
        }
        sleep(1);
    }
    return 0;
}

raise函数

给正在执行的进程发信号

#include <signal.h>
int raise(int sig);
示例代码:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void sig_handler(int signo)
{
    if (signo == SIGTSTP) {
        printf("SIGTSTP occured!\n");
    }
}

int main(void)
{
    if (signal(SIGTSTP, sig_handler) == SIG_ERR) {
        perror("signal sigtstp error");
    }

    printf("begin main running\n");
    int i = 0;
    while (i < 20) {
        printf("%d running %d\n", getpid(), i++);
        if (i == 10) {
       		raise(SIGTSTP);
        }
        sleep(1);
    }
    printf("end main running\n");
    return 0;
}

alarm函数

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
返回:0 或 以前设置的定时器时间余留秒数

由内核产生SIGALRM信号

参数为0,取消以前设置的定时器

示例代码:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

void sig_handler(int signo)
{
    if (signo == SIGALRM) {
        system("clear");
        time_t t = time(NULL);
        struct tm *timeInfo = localtime(&t);
        printf("%02d:%02d:%02d\n", timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
        alarm(1);
    }
}

int main(void)
{
    if (signal(SIGALRM, sig_handler) == SIG_ERR) {
        perror("signal sigalrm error");
    }

    int ret = alarm(5);
    ret = alarm(1);
    printf("ret is %d\n", ret);

    while (1) {
        pause();
    }

    return 0;
}

信号集

sigset_t

本质上是128位的集合,每一位代表一个信号

//清空信号集(每一位置为0)
int    sigemptyset(sigset_t *);

//添加信号到信号集中(该位置1)
int    sigaddset(sigset_t *, int);

//删除信号集中的信号(该位置0)
int    sigdelset(sigset_t *, int);

//信号集中的信号全置为1
int    sigfillset(sigset_t *);

//判断信号是否在信号集中(该位是否为1)
int    sigismember(const sigset_t *, int);

示例代码:

#include <stdio.h>
#include <signal.h>

void Test(const sigset_t *set, int signo)
{
    if (sigismember(set, signo) == 1) {
        printf("signo(%d)在信号集中\n", signo);
    } else {
        printf("signo(%d)不在信号集中\n", signo);
    }
}

int main(void)
{
    sigset_t set;

    sigemptyset(&set);
    Test(&set, 2);
    Test(&set, 3);

    sigaddset(&set, 2);
    Test(&set, 2);
    Test(&set, 3);

    sigfillset(&set);
    Test(&set, 2);
    Test(&set, 3);

    sigdelset(&set, 3);
    Test(&set, 2);
    Test(&set, 3);

    return 0;
}

信号屏蔽 与 未决信号

int sigpending(sigset_t *); //得到未决状态的信号
int sigprocmask(int, const sigset_t *restrict, sigset_t *restrict); //设置屏蔽信号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值