LinuxC 信号

信号及信号处理

一、信号概念

1.信号编号

kill -l
ps aux
ps ajx

linux中提供了64种信号编号,前32为经典信号(软件/操作系统),后32为实时信号(硬件/驱动)。

2.信号机制

man 7 signal

3.信号产生种类

1.特殊终端按键
ctl+c - SIGINT
ctl+z - SIGTSTP(暂停)
ctl+\ - SIGQUIT
2.硬件异常
除以0操作
访问非法内存
3.kill函数或kill命令

int kill(pid_t pid,int sig)
pid > 0
pid是信号欲送往的进程的标识。
pid ==0
信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
pid <- 1
信号将送往以-pid为组标识的进程。
pid == -1
 信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)

4.int raise(int sig) (自己向自己发信号)
5.void abort(void) (自己向自己发SIGABRT终止)
6.某种软件条件已发生
定时器alarm到时,每个进程只有一个定时器。

 unsigned int alarm(unsigned int seconds)

到时向进程发SIGALRM信号(运行结束)
返回未睡够秒数

4.信号产生原因

···

二、信号集和信号屏蔽字

1.进程处理信号行为

SIG_IGN
SIG_DFL
a signal handling function
1.默认处理动作(每个信号都有)
在这里插入图片描述 2.忽略
3.捕捉(用户自定义信号处理函数)

2.信号集处理函数

在这里插入图片描述
构造一个信号集,再注册到当前进程

3.PCB的信号集

在这里插入图片描述

4.sigprocmask(读取或更改进程的信号屏蔽字)

#include <signal.h>
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
返回值:若成功则返回0,若出错则返回-1

how参数
在这里插入图片描述

5.sigpending(读取当前的未决信号集)

#include <signal.h>
int sigpending( sigset_t * set)
通过set传出

例:

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

void printsigset(sigset_t *set)
{
    int i = 0;
    for(;i<32;i++){
        if(sigismember(set,i))
            putchar('1');
        else
            putchar('0');
    }
    puts("");    //打印换行
}

int main()
{
    sigset_t s,p;
    sigemptyset(&s);
    sigaddset(&s,SIGINT);
    sigprocmask(SIG_BLOCK,&s,NULL);
    while(1)
    {
        sigpending(&p);
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

SIGKILL和SIGSTOP不能被阻塞、捕捉和忽略!

三、信号捕捉设定(sigaction)

#include<signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
struct sigaction {
    void (*sa_handler)(int);   //捕捉函数(早期)
    void (*sa_sigaction)(int, siginfo_t *, void *);  //与上面的互斥,只能执行一个
    sigset_t sa_mask;   //设置阻塞信号集
    int sa_flags;       //SA_SIGINFO 或者 0,设置是用第一个还是第二个
    void (*sa_restorer)(void);
}

例:

//unix/linux提供的信号捕捉函数

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

int temp = 0;

void handler_sigint(int signo)
{
    printf("I an do_sig\n");
    printf("signo = %d\n",signo);
}

int main()
{
    struct sigaction act;
    act.sa_handler = handler_sigint;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;

    sigaction(SIGINT,&act,NULL);

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

捕捉函数执行过程
在这里插入图片描述

信号并不一定是产生后第一时间被响应(中断、异常、系统调用进入内核)

四、SIGUSR1 SIGUSR2实现父子进程同步输出

子进程继承父进程的信号屏蔽字和信号处理动作

#include<stdio.h>
#include<signal.h>
#include <unistd.h>
void pdosig(int num ,struct __siginfo * _siginfo, void * ptr)
{
    static int count = 0;
    printf("I am parent ,pid is %d,the proc : %d send signal: %d to me count is: %d\n",getpid(),_siginfo->si_pid,num,count);
    count+=2;
    sleep(1);
    kill(_siginfo->si_pid,SIGUSR2);
 
}
 
void cdosig(int num,struct __siginfo * _siginfo, void * ptr)
{
    static int count = 1;
    printf("I am child ,pid is %d,the proc : %d send signal: %d to me count is: %d\n",getpid(),_siginfo->si_pid,num,count);
    count+=2;
    sleep(1);
    kill(_siginfo->si_pid,SIGUSR1);
 
}
int main()
{
 
    struct  sigaction sig;
    int pid = fork();
    if(pid >0)
    {
        printf("i am father %d\n",getpid());
 
        sig.sa_sigaction = pdosig;
        sigemptyset(&sig.sa_mask);
        sig.sa_flags=0;
        sigaction(SIGUSR1,&sig,NULL);
 
    }
    else
    {
        printf("i am child %d\n",getpid());
        sig.sa_sigaction = cdosig;
        sigemptyset(&sig.sa_mask);
        sig.sa_flags=0;
        sigaction(SIGUSR2,&sig,NULL);
        kill(getppid(),SIGUSR1);
 
    }
    while(1)
    {
        sleep(1);
    }
    return 0;
}

五、C标准库信号处理函数

在这里插入图片描述
例:

//C标准库提供的信号捕捉函数
//在调用捕捉函数时,禁止调用不可重入函数,c库函数一般为不可重入函数

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

void do_sig(int n)
{
    printf("hello\n");
}

int main()
{
    signal(SIGINT,do_sig);

    while(1){
        printf("*********\n");
        sleep(1);
    }
    return 0;
}


//可重入函数不能含全局变量和静态本地变量
//集合fork,exec,wait于一体

#include<stdio.h>
#include<stdlib.h>

int main()
{
    system("要执行的东西*******");
    return 0;
}

可重入函数
不能含全局变量和静态本地变量

信号捕捉函数里应使用可重入函数,禁止调用不可重入函数

六、信号引起的竞态和异步I/O

1.时序竞态

int pause(void)
//是调用的进程挂起,直到有信号递达,如果递达信号是忽略,则继续挂起
int sigsuspend(const sigset_t *mask)
//以通过指定mask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程信号屏蔽字恢复原来的值
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sit(int n)
{

}
int main(void)
{
    struct sigaction act;

    act.sa_handler=do_sit;
    //act.sa_handler=SIG_IGN;     不行
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;

    sigaction(SIGUSR1,&act,NULL);

    pause();
    printf("hello");
    return 0;
}

2.mysleep函数实现:

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

void sig_alrm(int signo)
{

}

//返回的是未睡够的秒数
unsigned int mysleep(unsigned int nsecs)
{
    struct sigaction newact,oldact;
    sigset_t newmask,oldmask,suspmask;
    unsigned int unslept;

    newact.sa_handler=sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags=0;
    sigaction(SIGALRM,&newact,&oldact);

    sigemptyset(&newmask);
    sigaddset(&newmask,SIGALRM);
    sigprocmask(SIG_BLOCK,&newmask,&oldmask);

    alarm(nsecs);

    suspmask=oldmask;
    sigdelset(&suspmask,SIGALRM);
    sigsuspend(&suspmask);

    unslept=alarm(0);
    sigaction(SIGALRM,&oldact,NULL);

    sigprocmask(SIG_SETMASK,&oldmask,NULL);
    return(unslept);

}

int main(void)
{
    while(1){
        mysleep(2);
        printf("Two seconds passed\n");
    }

    return 0;
}

3.避免异步I/O的类型

sig_atomic_t
	//平台下的原子类型(取决于平台)
volatile
	//防止编译器开启优化选项时,优化对内存的读取

七、SIGCHLD信号处理

1.产生条件

子进程终止时
子进程接收到SIGCTOP信号停止时
子进程处在停止态,接受到SIGCONT后唤醒时

2.status处理方式

在这里插入图片描述

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

void sys_err(char *str)
{
    perror(str);
    exit(1);
}



void do_sig_child(int n)
{
    /*while(!(waitpid(0,NULL,WNOHANG)==-1)
            ;
    */
    int status;
    pid_t pid;

    while(waitpid(0,&status,WNOHANG)>0){
        if(WIFEXITED(status))    //是否为正常退出
            printf("child %d exit %d\n",pid,WEXITSTATUS(status));    //测试退出值
        else if(WIFSIGNALED(status))       //是否被信号终止
            printf("child %d cancel signal %d\n",pid,WTERMSIG(status));    //是被第几号信号终止
    }
}
int main()
{
    pid_t pid;
    int i;

    //阻塞SIGCHLD
    for(i=0;i<10;i++){
        if((pid=fork())==0)
            break;
        else if(pid<0)
            sys_err("fork");
    }
    if(pid==0){
        int n =8;
        while(n--){
            printf("child ID %d\n",getpid());
            sleep(1);
        }
        return i;
    }
    else if(pid>0){
        //先设置捕捉
        //再解除SIGCHLD
        struct sigaction act;
        act.sa_handler=do_sig_child;
        sigemptyset(&act.sa_mask);
        act.sa_flags=0;
        sigaction(SIGCHLD,&act,NULL);
        while(1){
            printf("parent ID %d\n",getpid());
            sleep(1);
        }
    }
    return 0;
}

八、向信号捕捉函数传参

1.sigqueue

int sigqueue(pid_t pid,int sig,const union sigval value)
union sigval{
	int sival_int;
	void *sival_ptr;  //有血缘关系
};

2.sigaction

九、信号中断系统调用

read阻塞时,信号中断系统调用:
1.返回部分读到的数据
2.read调用失败,erron设成EINER

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值