【Linux】信号(下)

信号集操作函数

是 Linux中用来操作"一组信号"的函数。这组信号用一个类型表示:sigset_t。

信号集操作函数 = 用来"增、删、查、清"信号集合的工具。

复制代码
#include <signal.h>
int sigemptyset(sigset_t* set);
初始化set所指向的信号集,使其中所有信号对应bit清零
表示该信号集不包含任何有效信号
int sigfillset(sigset_t* set);
初始化set所指向的信号集,使其中所有信号对应bit置位
表示该信号集的有效信号包括系统支持的所有信号。
!!注意!!:在使用sigset_t类型的变量之前,一定要调上述其中一种做初始化,使信号集处于确定状态。之后就可以调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
int sigaddset(sigset_t* set,int signo);
int sigdelset(sigset_t* set,int signo);
int sigismember(const sigset_t* set,int signo);

前四个函数都是成功返回0,出错返回-1。sigismember用于判断一个信号集的有效信号中是否包含某种信号,包含则返回1,不包含返回0,出错返回-1。
sigprocmask

可读取或更改进程的信号屏蔽字(阻塞信号集)。

复制代码
#include <signal.h>
int sigprocmask(int how,const sigset_t* set,sigset_t* oset);
返回值:成功为0,出错为-1.

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

//这里有点不懂具体过程???过几天看今天脑子太乱。
sigpending

复制代码
#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。
调用成功返回0,出错返回-1.

捕捉信号


sigaction

复制代码
#include <signal.h>
int sigaction(iint signo,const struct sigaction* act,struct sigaction* oact);
该函数可读取和修改与指定信号相关联的处理动作。

注意:当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来

的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

可重入函数

举例:

上面的例子会导致节点丢失,内存泄漏

重入:函数被不同执行流调用,可能在第一次调用还没返回时就再次进入该函数。

由于重入某个函数而造成错乱,这样的函数称为不可重入函数。相反,若一个函数只访问自己的局部变量或参数,则称为可重入函数。

满足下面任意一条即为不可重入函数:

1.调用了malloc或free(因为malloc用全局链表来管理堆。)

2.调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
volatile

优化情况下,输入ctrl+c ,2号信号被捕捉,执行自定义动作修改 flag=1 ,但 while 条件依旧满足,进

程继续运行.为什么?

编译器优化把变量存进寄存器里,逻辑判断时直接取寄存器里的变量而没有从内存里直数据,所以此时flag为0,诱发死循环。

cpp 复制代码
volatile int flag=0;
void handler(int signo)
{
    cout<<"catch a signal: "<<signo<<endl;
    flag=1;
}
int main()
{
    signal(2,handler);
    while(!flag)
    {}
    cout<<"process quit normal"<<endl;
    return 0;
}

SIGCHLD信号(了解)

进程等待时用wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以采用非阻塞轮询的方式是否有子进程结束且等待清理。采用前者父进程阻塞,无法处理自己的工作,采用后者父进程"一心二用",处理自己工作的同时还要时不时轮询一下,较为复杂。

其实,子进程在终止时会给父进程发SIGCHLD信号 ,该信号的默认处理方式是忽略,父进程可自定义其处理函数 ,这样父进程只需处理自己的工作即可,不必关心子进程了。子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

cpp 复制代码
#include <iostream>
#include <cstring>
#include <ctime>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

void handler(int signo)
{
    pid_t rid;
    while((rid=waitpid(-1,nullptr,WNOHANG))>0)
    {
        cout<<"I am a process: "<<getpid()<<" catch a signo:"<<signo
<<"child process quit: "<<rid<<endl;
    }
}
int main()
{
    signal(17,handler);
    for(int i=0;i<10;i++)
    {
        pid_t id=fork();
        if(id==0)
        {
            while(true)
            {
                cout<<"I am a child process: "<<getpid()<<", ppid: "<<getppid()<<endl;
                sleep(5);
                break;
            }
            cout<<"child quit!!!"<<endl;
            exit(0);
        }
        sleep(1);
    }
    while(true)
    {
        cout<<"I am a father process: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉 ,不会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户用sigaction函数自定义的忽略 通常没有区别,但这是一个特例。此方法对于Linux可用,不保证在其它UNIX系统上都可用。

举例:

cpp 复制代码
int main()
{
    //signal(17,handler);
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;     // 忽略 SIGCHLD
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGCHLD, &sa, nullptr);
    for(int i=0;i<10;i++)
    {
        pid_t id=fork();
        if(id==0)
        {
            while(true)
            {
                cout<<"I am a child process: "<<getpid()<<", ppid: "<<getppid()<<endl;
                sleep(5);
                break;
            }
            cout<<"child quit!!!"<<endl;
            exit(0);
        }
        sleep(1);
    }
    while(true)
    {
        cout<<"I am a father process: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}
相关推荐
一只自律的鸡18 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
猷咪18 小时前
C++基础
开发语言·c++
17(无规则自律)18 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
!chen18 小时前
linux服务器静默安装Oracle26ai
linux·运维·服务器
IT·小灰灰18 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧18 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q18 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳018 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾18 小时前
php 对接deepseek
android·开发语言·php
莫大33018 小时前
2核2G云服务器PHP8.5+MySQL9.0+Nginx(LNMP)安装WordPress网站详细教程
运维·服务器·nginx