欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux---信号

程序员文章站 2022-03-19 13:06:38
...

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

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

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

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;
}

Linux---信号

3.用户调用kill命令将信号发送给其他进程,进程调用kill函数将信号发送给另一个进程或进程组;
Linux---信号
函数:
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;
}

Linux---信号
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;
}

Linux---信号

信号在内核中的存在:
Linux---信号
递达(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:如何更改当前信号屏蔽字;
Linux---信号
读取进程的当前信号屏蔽字由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;
}

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

如何去捕捉信号?

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

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;
}
相关标签: 信号