Linux---信号

信号对于我们来说已经很熟悉了,走在路上红灯亮起,这时我们就停下脚步,绿灯亮起,又继续前行;当水壶哨声响起的时候,这时我们就过去关掉电源……
那么我们为什么会对那种现象进行识别,并做出一系列的反应呢?
这是因为大脑能记住生活中的这种信号进行识别并做出动作。那么进程也是如此。

信号的产生:
1.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程;

终止程序:在键盘按下ctrl+\ 组合键产生信号SIGQUIT,SIGQUIT默认动作是终止程序并Core Dump(核心转储).
Core Dump:当一个进程在异常终止的时候,操作系统会将内存中进程的有效数据拷贝到磁盘上,文件名通常是core。
查询core文件大小命令:ulimit -a
修改core 文件大小命令:ulimit -c 大小
Core Dump:可以定位程序错误
这里写图片描述

2.硬件异常产生信号(如:除数为0,无效的内存引用等),硬件检测到并通知内核,内核会向当前进程发送信号;

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

int main(int argc,char* argv)
{
    if(argc!=2){
        printf("Usage:%s,symbol",argv[0]);
        exit(1);
    }
    return 0;
}

这里写图片描述

3.用户调用kill命令将信号发送给其他进程,进程调用kill函数将信号发送给另一个进程或进程组;
这里写图片描述
函数:
int kill(pid_t pid,int signo):给指定进程发指定信号

int main(int argc,char* argv[])
{
        if(argc!=3){
                printf("Error:%s,signo,pid\n",argv[0]);
                exit(1);
        }
        kill(atoi(argv[2]),atoi(argv[1]));
        return 0;
}

这里写图片描述
int raise(int signo):给当前进程发送特定信号

void abort(void):使当前进程接收到信号异常终止

4.当检测到某些软件条件已经产生,将其通知有关进程时产生信号。

int count=0;
void handler(int signo)
{
        printf("signo:%d count:%d\n",signo,count);
        exit(1);
}
int main(int argc,char* argv[])
{
        signal(14,handler);
        alarm(1);//信号没产生前的一秒,将数据累加多少次
        while(1){
                printf("count=%d\n",count);
                count++;
        }
        return 0;
}

这里写图片描述

信号在内核中的存在:
这里写图片描述
递达(Delivery):实际执行信号的处理动作;
阻塞(Block):被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作;
未决(Pending):信号从产生到递达之间的状态。

信号集:
每个信号只有一个bit的未决标志或阻塞标志,不记录该信号产生多少次,因此可以用相同的数据类型sigset_t来存储,sigset_t称为信号集。

使用者只能调用以下函数对sigset_t变量进行操作,不应该对它的内部数据进行解释。

int sigemptyset(sigset_t* set);
初始化set所指向的信号集,使其中所有信号的对应bit位清零,表示该信号集不包含任何有效信号;
int sigfillset(sigset_t* set);
初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包含系统支持的所有信号;
int sigaddset(sigset* set,int signo);
将一个信号signo添加到set所指向的信号集中;
int sigdelset(sigset_t* set,int signo);
将一个信号signo从set所指向的信号集中删除;
以上四个函数都是成功返回0,失败返回-1.
int sigismember(sigset_t* set,int signo);
判断一个信号signo是否在set所指向的信号集中。
sigismember函数是一个bool函数,包含则返回1,不包含则返回0,出错返回-1.

读取或更改进程的信号屏蔽字函数(阻塞信号集):
int sigprocmask(int how,const sigset_t *set,sigset_t* oset);
参数:how:如何更改当前信号屏蔽字;
这里写图片描述
读取进程的当前信号屏蔽字由oset参数传出,若set和oset都为非空,则将原来的信号屏蔽字备份到oset里。

读取当前进程的未决信号集:
int sigpending(sigset_t* set);
函数返回信号集,其中的各个信号对于调用进程是阻塞的而不能递达,因此也一定是当前未决的。信号集由set参数返回。
成功返回0,失败返回-1.

void Print(sigset_t* set)
{
        int i=0;
        for(;i<32;++i){
        //判断信号是否在信号集中,在的话就将对应比特位置1
                if(sigismember(set,i)){
                        putchar('1');
                }
                else{
                        putchar('0');
                }
        }
        printf("\n");
}

int main()
{
        sigset_t sig,psig;
        //对信号集进行初始化
        sigemptyset(&sig);
        //将信号SIGINT添加到信号集中
        sigaddset(&sig,SIGINT);
        //设置进程的信号屏蔽字
        sigprocmask(SIG_BLOCK,&sig,NULL);
        while(1){
            //获取当前进程的未决信号集
                sigpending(&psig);
                Print(&psig);
                sleep(1);
        }
        return 0;
}

这里写图片描述
捕捉信号:
什么时候对信号进行捕捉是合适的时机?
这里写图片描述
从上图可知:捕捉信号的最佳时机在进程从内核态返回到用户态的时候,这个时候会进行一次信号的检测和处理,如果信号的处理动作是用户自定义函数,信号递达时调用这个处理函数,这个过程称为信号的捕捉。

如何去捕捉信号?

读取或修改与指定信号相关联的处理动作函数:

int sigaction(int signo,const struct sigaction* act,struct sigaction* oact);

结构体:struct sigaction
{
void(*)(int) sa_handle;
sigset_t sa_mask;
int sa_flags;
}

act:若非空,则根据act修改该信号的处理动作;
oact:若非空,则根据oact传出原来的信号处理动作。
返回值:成功返回0,失败返回-1.

使调用进程挂起直到有信号递达函数:

int pause(void)

如果信号处理动作是终止进程,进程终止,函数不返回;
如果信号处理动作是忽略,进程还是处于挂起状态,函数不返回;
如果信号处理动作是捕捉,则调用信号处理函数,成功pause返回-1,出错error设置为EINTR。

思考:调用pause()函数之后,信号递达,设置的信号处理函数啥都不做,那么信号处理函数有没有必要存在?

有必要。
调用pause()函数之后,进程处于挂起状态,当信号递达的时候信号自动添加到信号屏蔽字中(当该信号再次到来的时候会被阻塞到当前处理结束),信号处理函数调用完毕之后信号解除屏蔽,pause()函数返回,进程继续执行;如果没有信号处理函数,pause()函数将使进程一直处于挂起状态。

竞态条件:由于程序执行的时序问题导致的错误。

int sigsuspend(const sigset_t* sigmask )
通过指定的sigmask来临时解除对某些信号的屏蔽。没有成功返回值,调用信号处理函数返回-1,error设置为EINTR。

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

void handler(int signo)
{
        printf("get a signo:%d\n",signo);
        exit(1);
}

void mysleep(int seconds)
{
        struct sigaction new, old;
        sigset_t newmask,oldmask,midmask;
        unsigned int count=0;

        new.sa_handler=handler;
        sigemptyset(&new.sa_mask);
        new.sa_flags=0;
        sigaction(SIGALRM,&new,&old);

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

        alarm(seconds);
        midmask=oldmask;
        sigdelset(&midmask,SIGALRM);
        sigsuspend(&midmask);

        count=alarm(0);
        sigaction(SIGALRM,&old,NULL);
        sigprocmask(SIG_SETMASK,&oldmask,NULL);
}

int main()
{
        while(1){
                printf("I like eating icecream!\n");
                mysleep(1);
        }
        return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39295755/article/details/79875991
今日推荐