【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;
}
相关推荐
栈与堆1 分钟前
LeetCode 19 - 删除链表的倒数第N个节点
java·开发语言·数据结构·python·算法·leetcode·链表
一路向北·重庆分伦3 分钟前
03-01:MQ常见问题梳理
java·开发语言
txinyu的博客7 分钟前
结合游戏场景理解,互斥锁,读写锁,自旋锁,CAS / 原子变量,分段锁
开发语言·c++·游戏
sophie旭10 分钟前
内存泄露排查之我的微感受
前端·javascript·性能优化
hugerat13 分钟前
在AI的帮助下,用C++构造微型http server
linux·c++·人工智能·http·嵌入式·嵌入式linux
YIN_尹13 分钟前
【MySQL】数据类型(上)
android·mysql·adb
阿里嘎多学长15 分钟前
2026-01-11 GitHub 热点项目精选
开发语言·程序员·github·代码托管
yuanyikangkang16 分钟前
STM32 lin控制盒
开发语言
txinyu的博客1 小时前
HTTP服务实现用户级窗口限流
开发语言·c++·分布式·网络协议·http