【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号

这篇具有很好参考价值的文章主要介绍了【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、信号的捕捉

1.用户态和内核态

用户态的的时候,进行以下操作:1.操作系统自身的资源(getpid,waitpid…)2.硬件资源(printf, write,read)

用户为了访问内核或者硬件资源,必须通过系统调用完成访问。实际执行系统调用”人是“进程”,但是身份其实是内核。往往系统调用比较费时间一些,所以尽量避免频繁调用系统调用

CPU中有两类寄存器:1.可见寄存器2.不可见寄存器。凡是和当前进程强相关的,上下文数据都保存在寄存器中。CR3寄存器表征当前进程的运行级别;0:内核态,3表示用户态

我一直不太理解:我是一个进程,怎么跑到OS中执行方法呢?

每个进程都有自己独立的用户级页表,内核级页表只有一份就够了

每一个进程都有自己的地址空间(用户空间独占)内核空间(被映射到了每一个进程的34G)。进程要访问OS的接口,其实]只需要在自己的地址空间上进行跳转就可以了!!每一个进程都有34GB,都会共享一个内核级页表,无论进程如何切换,会不会更改任何的[3.4]。用户,凭什么能够执行访问内核的接口或者数据呢?系统调用接口,起始的位置会帮你做的!Int 80 --陷入内核

2.内核如何实现信号的捕捉

信号产生的时候,不会被立即处理,而是在合适的时候。从内核态返回用户态的时候,进行处理,说明曾经我一定是先进入了内核态!----系统调用,进程切换

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

线程通过系统调用陷入内核,完成了从用户态到内核态的转变你,然后遍历block和pending表,以及映射的hander,对信号进行默认/忽略/自定义的捕捉,对于自定义捕捉,操作系统通过特定的调用,将自己的身份重新改为用户态,执行自定义函数,执行完毕之后,又通过特殊的系统调用sigreturn再次回到内核,继续进行信号检测,然后返回用户模式,从上次被中断的地方继续向下执行。

3.sigaction

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo

是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传

出该信号原来的处理动作。act和oact指向sigaction结构体:

将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动

作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回

值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信

号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <signal.h>

void Count(int cnt)
{
    while(cnt)
    {
        printf("cnt: %2d\r", cnt);
        fflush(stdout);
        cnt--;
        sleep(1);
    }
    printf("\n");
}

void handler(int signo)
{
    std::cout << "get a signo: " << signo << "正在处理中..." << std::endl;
    Count(20);
}

int main()
{
    struct sigaction act, oact;
    act.sa_handler = handler;
    act.sa_flags = 0;
    // 当我们正在处理某一种信号的时候,我们也想顺便屏蔽其他信号,就可以添加到这个sa_mask中
    sigemptyset(&act.sa_mask);
    // sigaddset(&act.sa_mask, 3);
    sigaction(SIGINT, &act, &oact);

    while (true)
        sleep(1);
    return 0;
}

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。 sa_flags字段包含一些选项,本章的代码都把sa_flags设为0,sa_sigaction是实时信号的处理函数

二、可重入函数

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。

像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。想一下,为什么两个不同的控制流程调用同一个函数,访问它的同一个局部变量或参数就不会造成错乱?

如果一个函数符合以下条件之一则是不可重入的:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。

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

三、volatile

volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作

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

volatile int quit = 0;

void handler(int signo)
{
    printf("%d 号信号,正在被捕捉!\n",signo);
    printf("quit: %d", quit);
    quit = 1;
    printf("->%d\n", quit);
}

int main()
{
    signal(2, handler);
    while (!quit);
    printf("注意,我是正常退出的\n");
    return 0;
}

不加volatile就会一直休眠

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

加了之后,收到2号信号后直接退出

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

四、SIGCHLD信号

wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。

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

请编写一个程序完成以下功能:父进程fork出子进程,子进程调用exit(2)终止,父进程自定 义SIGCHLD信号的处理函数,在其中调用wait获得子进程的退出状态并打印。

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

void hanlder(int signo)
{
    pid_t id;
    int status;
    if (id = waitpid(-1, &status, 0) > 0)
    {
        std::cout << "wait child process success,lastcode: " << ((status >> 8) & 0xff) << std::endl;
    }
    std::cout << "child is quit" << std::endl;
}
int main()
{
    signal(SIGCHLD, hanlder);
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "child create seccuess" << std::endl;
        sleep(3);
        exit(2);
    }
    while (true)
    {
        std::cout << "father process is doing other things" << std::endl;
        sleep(1);
    }
    return 0;
}

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction

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

请编写程序验证这样做不会产生僵尸进程

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    signal(SIGCHLD, SIG_IGN);
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "child create success" << std::endl;
        sleep(3);
        exit(2);
    }

    int status = 0;
    id = waitpid(id, &status, 0);
    if (id > 0)
    {
        std::cout << "wait child process success,lastcode: " << ((status >> 8) & 0xff) << std::endl;
    }

    std::cout << "child is quit" << std::endl;
    return 0;
}

【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号,Linux系统编程,linux,服务器,SIGCHLD信号,volatile,可重入函数,信号的捕捉,sigaction文章来源地址https://www.toymoban.com/news/detail-761610.html

到了这里,关于【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Linux系统调用之signal,sigaction函数 (捕捉指定的信号,自定义/默认处理该信号)

    如果,想要深入的学习Linux系统调用中的signal,sigaction函数,还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令: 即可查阅到完整的资料信息。 signal()是一个用于处理Unix和类Unix操作系统(如Linux)中的信号的系统调用。信号是用于在进程间或者进程内部传递通知或者

    2024年02月02日
    浏览(14)
  • 【Linux】volatile | SIGCHLD | 多线程概念

    【Linux】volatile | SIGCHLD | 多线程概念

    在vscode中,创建signal.c文件 故意在while中没有写代码块,让编译器认为在main中,quit只会被检测 运行可执行程序后,当输入 2号信号时,调用自定义方法将quit置为1,跳出while循环 编译器有对应的编译优化级别 -O1 -O2 -O3 在makefile中,添加-O2的优化级别 再次执行可执行程序时,输

    2024年02月05日
    浏览(7)
  • 【Linux】进程信号——进程信号的概念和介绍、产生信号、四种产生信号方式、阻塞信号、捕捉信号、阻塞和捕捉信号的函数

    【Linux】进程信号——进程信号的概念和介绍、产生信号、四种产生信号方式、阻塞信号、捕捉信号、阻塞和捕捉信号的函数

      在Linux中,进程信号是一种异步的事件通知机制,用于通知进程某个事件已经发生。它是进程间通信的一种方式,可以用来控制进程的行为。   当一个进程收到信号时,操作系统会中断该进程的正常控制流程,并执行相应的处理函数。进程收到信号后有三种处理方式:

    2024年02月02日
    浏览(16)
  • Linux中的alarm和setitimer定时器函数以及信号捕捉函数signal和sigaction

    Linux中的alarm和setitimer定时器函数以及信号捕捉函数signal和sigaction

    通过 man 2 alarm 查看alarm函数的详细信息。 功能: 设置定时器(闹钟)。从调用这个函数开始进行倒计时,倒计时结束之后(变为0),函数会给当前的进程发送 SIGALARM 信号。 参数: seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。取

    2024年02月03日
    浏览(9)
  • Linux——SIGCHLD信号——第17号信号

    Linux——SIGCHLD信号——第17号信号

    目录 什么是僵尸进程? 僵尸进程的目的? 如何避免子进程成为僵尸进程? 小贴士: 提到SIGCHLD信号,就不得不先说一个有关僵尸状态的进程知识点了。 什么是僵尸进程?         首先内核会释放终止进程(调用了exit系统调用)所使用的所有存情区,关闭所有打开的文件等,但内

    2024年02月13日
    浏览(8)
  • Java入门-可重入锁

    Java入门-可重入锁

    什么是可重入锁? 当线程获取某个锁后,还可以继续获取它,可以递归调用,而不会发生死锁; 可重入锁案例 程序可重入加锁 A.class,没有发生死锁。 sychronized锁 运行结果 ReentrantLock 运行结果 如何保证可重入 当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里

    2024年02月22日
    浏览(12)
  • java~理解可重入锁

    在Java中,可重入锁(Reentrant Lock)是一种同步机制,允许线程在持有锁的情况下再次获取该锁,而不会被自己所持有的锁所阻塞。也就是说,一个线程可以多次获得同一个锁,而不会出现死锁的情况。 可重入锁在多线程编程中非常有用,它允许线程在访问共享资源时多次获取

    2024年02月09日
    浏览(8)
  • 并发编程之可重入锁ReentrantLock

    并发编程之可重入锁ReentrantLock

    大家都知道在并发编程中一般会用到多线程技术,多线程技术可以大大增加系统QPS/TPS。但是在一些特殊的业务场景下我们需要限制线程的并发数目,比如秒杀系统、多种商品金额叠加运算等等都是需要限制线程数量。特别是在分布式微服务架构,多线程同步问题尤为明显。一

    2023年04月25日
    浏览(22)
  • curator实现的zookeeper可重入锁

    Curator是一个Apache开源的ZooKeeper客户端库,它提供了许多高级特性和工具类,用于简化在分布式环境中使用ZooKeeper的开发。其中之一就是可重入锁。 Curator提供了 InterProcessMutex 类来实现可重入锁。以下是使用Curator实现ZooKeeper可重入锁的示例: import org.apache.curator.framework.Curato

    2024年02月15日
    浏览(11)
  • ReentrantLock是如何实现可重入性

    1 问题 ReentrantLock是如何实现可重入性的? 并且在进入锁内之前进行了哪些判断? 2 方法 // Sync继承于AQS abstract static class Sync extends AbstractQueuedSynchronizer {  ... } // ReentrantLock默认是非公平锁 public ReentrantLock() {        sync = new NonfairSync(); } // 可以通过向构造方法中传true来实现公

    2024年02月07日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包