Linux 第三十一章

news2024/9/23 23:25:05

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

核心转储

ulimit指令

信号的保存

sigset_t

sigprocmask()

动态观察指定信号的pending

sigpending

sigismember

信号的处理

sigaction

struct sigaction

信号的其他补充问题

volatile:关键字

子进程退出的时候,需要向父进程发送信号的SIGCHILD


核心转储

ulimit指令

ulimit 命令用于查看和修改当前用户的资源限制。Linux 系统中的资源限制指的是系统或进程所允许的最大资源使用量,例如 CPU 时间、文件大小、进程数等。

使用 ulimit 命令可以查看当前用户的资源限制,命令格式如下:

ulimit [选项] [参数]
常用的选项包括:

* -a:显示所有资源限制。
* -c:设置或显示进程核心转储文件的大小限制。
* -f:设置或显示可创建的文件的最大大小。
* -n:设置或显示单个进程可以打开的文件描述符数量限制。
* -u:设置或显示单个用户可以拥有的最大进程数量限制。

例如,要查看当前用户可以打开的最大文件描述符数量,可以使用以下命令:
ulimit -n
要将进程的最大文件大小限制设置为100MB,可以使用以下命令:
ulimit -f 100000

需要注意的是,ulimit 命令只能影响当前 shell 中的进程及其子进程,对于其他进程不会产生影响。如果需要在系统上全局修改资源限制,需要修改系统配置文件 /etc/security/limits.conf 和 /etc/sysctl.conf 等。

当一个程序出现了严重的错误(例如内存越界、除以零等),导致程序无法正常继续执行时,系统会将当前程序的内存映像保存到一个称为“核心转储文件(core dump)”的文件中,以便进行后续的调试和分析。

在 Linux 系统中,可以使用 ulimit 命令设置或显示进程核心转储文件的最大大小限制。通常情况下,系统会将核心转储文件保存在当前目录下,文件名以 core.[进程ID] 的形式命名。例如,如果进程ID是1234,则核心转储文件的文件名为 core.1234。

需要注意的是,核心转储文件可能会包含敏感数据,例如密码、私钥等,因此在进行调试时需要格外小心,确保不会泄露敏感信息。同时,在生产环境中应该关闭核心转储功能,以减少安全风险。

云服务器一般是关闭了核心转储

信号的保存

信号的处理的三种方式,我们可以使用signal(signum,handler)

1. 如果 handler 为 SIG_DFL,表示将信号的处理方式恢复为默认操作。

2. 如果 handler 为 SIG_IGN,表示忽略该信号。

3. 如果 handler 不是 SIG_DFL 或 SIG_IGN,则表示安装一个新的信号处理函数。进行自定义捕捉

忽略算不算处理了这个信号呢?已经处理该信号,结果就是忽略

信号从产生到递达之间的状态,称为信号未决(Pending)。

信号写入到位图时,叫做信号未决

进程可以选择阻塞 (Block )某个信号。

被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.

未决之后,暂时不进行递达,直到解除对信号的阻塞

注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作

进程PCB有三个字段和信号相关

pending:未决位图表

handler:对应信号的处理方法,是一个函数指针数组,数组的内容就是对应的信号处理方法

block:信号阻塞表

比特位的位置:表示信号编号

信号解除阻塞时,将立即信号抵达

sigset_t

sigset_t 是一个数据类型,用于表示信号集。sigset_t 是一个位向量,每个位对应一个可能的信号编号。使用 sigset_t 可以方便地管理和操作信号集合。

sigset_t 数据类型通常作为函数参数或返回值,用于设置和获取进程的信号阻塞集合、修改信号处理函数的行为等。

以下是一些常用的与 sigset_t 相关的函数:

1. sigemptyset():将信号集清空,即将所有信号位设为 0。

2. sigfillset():将信号集填满,即将所有信号位设为 1。

3. sigaddset():向信号集中添加指定的信号。

4. sigdelset():从信号集中删除指定的信号。

5. sigismember():检查指定的信号是否在信号集中。

6. sigprocmask():用于设置或获取进程的信号阻塞集合。可以阻塞或解除阻塞指定的信号。

sigprocmask()

sigprocmask() 函数用于获取或设置进程的信号阻塞集合。信号阻塞集合是一组信号,在阻塞期间不会递送给进程。

该函数的原型如下:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigprocmask() 函数有三个参数:

* how:表示要进行何种方式的操作,可选值为 SIG_BLOCK、SIG_UNBLOCK 和 SIG_SETMASK。
    * SIG_BLOCK:将 set 指向的信号集中的信号添加到进程的信号阻塞集合中。
    * SIG_UNBLOCK:将 set 指向的信号集中的信号从进程的信号阻塞集合中移除。
    * SIG_SETMASK:将进程的信号阻塞集合替换为 set 指向的信号集。
* set:指向一个信号集,包含要添加或删除的信号。
* oldset:指向一个信号集,用于保存原来的信号阻塞集合。如果该参数为 NULL,则表示不需要保存原来的信号阻塞集合。
sigprocmask() 函数返回值为 0 表示成功,返回值为 -1 表示出现错误

SIG_BLOCK 用于向当前的信号阻塞集合中添加新的信号,而 SIG_SETMASK 用于完全替换当前的信号阻塞集合为新的信号集。

如果我们把该进程0-31信号全部在block表中设置为阻塞,我们还能够杀死该进程吗

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;
int main()

{
    cout<<getpid()<<endl;
    sigset_t block;
    sigset_t oblock;

    sigemptyset(&block);
    sigemptyset(&oblock);
    int signo=1;

    for(;signo<=31;signo++)
        sigaddset(&block,signo);//在这里设置了对1-31号信号的屏蔽吗?没有,只是对这个block变量进行操作
    sigprocmask(SIG_SETMASK,&block,&oblock);//这一步才是对1-31号信号进行屏蔽

    while(1)
    {
        cout<<"信号已经被屏蔽,你无法处理我"<<endl;
        sleep(1);
    }


}

我们可以发现,尽管我们把1-31号信号在该进程全部在block中设置成阻塞,我们向该进程发送其他终止信号时,都不会终止该进程,但是我们发送9号信号时,仍然可以终止该进程,说明9号信号不会被我们设置成阻塞就影响(19信号也可以),9号信号可以称为管理员信号

动态观察指定信号的pending

因为,OS处理信号非常的快,我们不容易看出pending的变化,但我们可以让指定信号先阻塞,然后去观察

sigpending

sigpending 是一个函数,用于获取当前被阻塞但是已经产生的信号集合。这个函数可以帮助程序判断哪些信号在阻塞状态下已经产生,但是还未被处理。

下面是 sigpending 函数的原型:

int sigpending(sigset_t *set);
sigpending 函数接受一个指向 sigset_t 类型的指针作为参数,
该参数用于存储当前被阻塞但已经产生的信号集合。

调用 sigpending 函数将阻塞的信号集合存储到传入的 set 指针所指向的位置。如果有任何被阻塞的信号已经产生,它们会在 set 中被设置为相应的位。

sigismember

sigismember 是一个函数,用于检查一个给定的信号是否已经设置在某个信号集中。

下面是 sigismember 函数的原型:

int sigismember(const sigset_t *set, int signum);
sigismember 函数接受两个参数:指向 sigset_t 类型的指针和一个整数参数 signum。
它会检查 signum 是否已经被设置在信号集合 set 中,并返回 1(表示是)或 0(表示否)。

事例

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;



void handler(int signum)
{
    cout<<signum<<getpid()<<endl;
    //exit(0);
}
void printpending(const sigset_t& pending )
{
    for(int signo=31;signo>0;signo--)
    {
        if(sigismember(&pending,signo))
        {
            cout<<"1";
        }
        else
        {
            cout<<"0";
        }
    }
    cout<<endl;
}

int main()
{
    signal(2,handler);
    cout<<getpid()<<endl;
    //1.屏蔽2号信号
    sigset_t block;
    sigset_t oblock;
    sigemptyset(&block);    
    sigemptyset(&oblock);
    sigaddset(&block,2);//在这里设置了对2号信号的屏蔽吗?没有,只是对这个block变量进行操作
    sigprocmask(SIG_BLOCK,&block,&oblock);//这一步才是对2号信号进行屏蔽

    //2.让进程不断获取当前进程的pending
    int cnt=0;
    sigset_t pending;
    while(true)
    {
        sigpending(&pending);
        printpending(pending);
        sleep(1);
        cnt++;
        
        if(cnt==15)
        {
            cout<<"解除对2号信号的屏蔽,2号信号准备递达"<<endl;
            sigprocmask(SIG_SETMASK,&oblock,nullptr);
        }
    }
}


[BCH@hcss-ecs-6176 2_1]$ ./test_signal 
13127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^C0000000000000000000000000000010//ctrl+c向该进程发送2号信号时,由于2号信号在block中阻塞,所以2还信号在pending表中,处于未决状态,
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
解除对2号信号的屏蔽,2号信号准备递达//当阻塞被解除,2号信号马上就会递达,该进程的2号信号就不会处于未决状态,所以为0,
213127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^C213127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^\Quit

信号的处理

信号在合适的时候被处理—什么时候

进程从内核态返回到用户态的时候,进行信号的检测和信号的处理

用户态是一种受控的状态,能够访问资源有限的

内核态一种操作系统的工作状态,能够访问大部分系统资源

可以通过cs寄存器后两个比特位来表示系统处于状态,1是内核态,3用户态

进程从内核态返回用户态的时候,进行信号的检测和信号处理

无论代码中是否使用系统调用,整个进程生命周期里,会有很多次进程间切换,切换了一定会从内核态返回用户态,当前进程就会有多次信号捕捉的机会

sigaction

sigaction是一个用于设置信号处理器的系统调用函数,它在Linux中被广泛使用。

函数原型如下:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数说明:

* signum:指定要设置处理器的信号编号。
* act:指向struct sigaction结构的指针,用于指定新的信号处理器和标志。
* oldact:如果不为NULL,则旧的信号处理器信息将被写入此结构中。
struct sigaction结构包含以下字段:
* sa_handler:函数指针,指定信号处理函数。
* sa_flags:指定信号处理的选项,如SA_RESTART、SA_NODEFER等。
* sa_mask:指定一个信号屏蔽集合,用于阻塞其他信号。
通过调用sigaction函数,可以为特定信号设置自定义的信号处理函数。当指定的信号发生时,系统将执行该处理函数来处理信号。
struct sigaction
struct sigaction {
    void (*sa_handler)(int);  // 指定信号处理函数的函数指针
    void (*sa_sigaction)(int, siginfo_t *, void *);  // 用于接收信号附加信息的处理函数指针
    sigset_t sa_mask;  // 用于阻塞的信号集合
    int sa_flags;  // 指定信号处理的选项
    void (*sa_restorer)(void);  // 已经废弃,用于提供旧的系统调用信号语义的函数指针
};

在Linux中,处理一个信号时,默认情况下会自动屏蔽相同信号。也就是说,如果当前进程正在处理某个信号,而此时又收到了同样的信号,则新的信号不会被立即处理,而是被暂时屏蔽,直到当前正在处理的信号处理完毕后才会处理

例如:我们这里使用sigaction系统调用对2号信号的处理方法进行自定义,我们在自定义处理方法中循环输出,当前进程的未决信号集,我们第一次发送2号信号时,未决信号集中2号信号比特位将被置0,但是第二次发送2号信号,将不被处理,因为第一次发送的信号还没有处理完。

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;

void printsigpending(const sigset_t& pending);

void handler(int signo)
{
    cout<<"get a sig: "<<signo<<endl;
    while(true)
    {
        sigset_t pengding;
        sigpending(&pengding);
        printsigpending(pengding);
        sleep(1);
    }
}

void printsigpending(const sigset_t& pending)
{
    for(int signo=31;signo>0;signo--)
    {
        if(sigismember(&pending,signo))
            cout<<"1";
        else
            cout<<"0";
    }
    cout<<endl;
}

int main()
{
    cout<<"pid: "<<getpid()<<endl;
    struct sigaction act,oact;
    act.sa_handler=handler;
    sigisemptyset(&act.sa_mask);
    //sigaddset(&act.sa_mask,3);//我们可以设置struct sigaction中的sa_mask,这样可以处理这个信号时,不仅能够屏蔽相同信号,还能屏蔽其他信号
    sigaction(2,&act,&oact);


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

}

信号的其他补充问题

重入不可重入是函数的特点(我们平常遇到的大部分函数都是不可重入的)

volatile:关键字

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

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;


volatile int flag=0;//防止编译器过度优化
void handler(int signo)

{
cout<<"signo:"<<signo<<endl;
flag=1;
cout<<"change flag :"<<flag<<endl;
}


int main()

{
signal(2,handler);
while(!flag);
cout<<"normal quit"<<endl;
}

优化情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 ,但是 while 条件依旧满足,进
程继续运行!但是很明显flag肯定已经被修改了,但是为何循环依旧执行?很明显, while 循环检查的flag,
并不是内存中最新的flag,这就存在了数据二异性的问题。 while 检测的flag其实已经因为优化,被放在了
CPU寄存器当中。如何解决呢?很明显需要 volatile

子进程退出的时候,需要向父进程发送信号的SIGCHILD

wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

进程是资源分配的基本单位,线程是调度的基本单位

  🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1678163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

立创EDA绘制电路原理图

1、下载立创EDA并安装注册&#xff0c;这里我用的是标准版 2、在账号名右键&#xff0c;新建工程 3、以单片机时钟电路为例 4、首先绘制原理图 &#xff08;1&#xff09;放置元器件&#xff0c;以AT89C52为例&#xff0c;在元件库中查找单片机&#xff0c;找到后确认封装&…

机器人工具箱学习(三)

一、动力学方程 机器人的动力学公式描述如下&#xff1a; 式中&#xff0c; τ \boldsymbol{\tau} τ表示关节驱动力矩矢量&#xff1b; q , q ˙ , q \boldsymbol{q} ,\; \dot{\boldsymbol { q }} ,\; \ddot{\boldsymbol { q }} q,q˙​,q​分别为广义的关节位置、速度和加速…

JavaScript对象的声明

JS声明对象的语法 1 通过new Object()直接创建对象2 通过 { }形式创建对象 1 通过new Object()直接创建对象 代码 var person new Object(); // 给对象添加属性并赋值 person.name"张小明"; person.age10; person.foods["苹果","橘子","香…

吞吐量 和 延时的关系

关于吞吐量/吞吐率、延时&#xff0c;你可以通过 Jmeter中的”聚合报告“和”用表格查看报告“来获取。 Throughput 越大&#xff0c;Latency 越差&#xff1a;因为请求过多&#xff0c;系统繁忙导致响应速度降低。Latency 的值越小说明能支持的 Throughput 越高&#xff1a;L…

pdfMake,xlsx-js-style,elementTable表格导出大量数据的pdf和xslx表格

使用渲染dom传递给xlsx或将dom转canvas在传给jspdf数据量大都会造成页面负载过大 所以导pdf和xlsx都使用数据传递给pdfMake,xlsx-js-style&#xff0c;pdf涉及分页与合并单元格 一.pdf npm并引入pdfMake和其字体包&#xff08;记录时使用版本0.2.10 import pdfMake from &qu…

仅1年!荣登中科院1区经济类SSCI宝座!影响因子3连涨,创刊时间不长但口碑飙升!

【SciencePub学术】今天小编给大家带来了一本经济学领域的高分优刊解读&#xff0c;创刊时间不长&#xff0c;但影响因子3连涨现已高达8.5&#xff0c;JCR1区&#xff0c;中科院1区&#xff0c;领域相符的学者可考虑&#xff01; Oeconomia Copernicana 1 期刊概况 【期刊简介…

大龄程序员是否要入职嵌入式python岗位?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Python的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 是否要做嵌入式 Python 取决于…

vue(九) 生命周期 v3.0和v2.0对比,父子组件生命周期的执行顺序

文章目录 生命周期vue2.0生命周期1.图示2.生命周期解释说明3.代码示例 vue3.0生命周期1.图示2.生命周期解释说明3.代码示例 父子组件中生命周期执行顺序v.3和v2.0生命周期对比 生命周期 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xff0c;比如设置好数据侦听…

批量下载huggingface的仓库全部权重文件

下载huggingface的仓库全部权重文件 配置和下载git-lfs **ubuntu:**sudo apt-get install git-lfs 其他&#xff1a; 下载git-lfs Releases git-lfs/git-lfs (github.com) 配置&#xff1a; export PATH$PATH://home/software/lfs/git-lfs-3.5.1/ # 其中目录为你文件夹的目…

tomcat启动闪退解决办法

tomcat启动闪退解决办法 1. 端口号被占用2. 检查电脑环境配置2.1 如何配置电脑的环境变量&#xff1f; windows系统下&#xff0c;tomcat安装好以后&#xff0c;双击bin文件下startup.bat后&#xff0c;tomcat没能按照预期打开&#xff0c;反而闪退了。 导致这种情况发生&…

python高级爱心代码

python高级爱心代码实现&#xff1a; import turtle import random # 设置画布 screen turtle.Screen() screen.bgcolor("black") # 创建画笔 pen turtle.Turtle() pen.speed(0) pen.color("red") pen.penup() # 移动画笔到起始位置 pen.goto(0, -20…

刘邦痛恨的叛徒雍齿,为何后来还被封了侯?

雍齿&#xff0c;原是沛县的世族出身&#xff0c;家庭往上追溯几代&#xff0c;也曾经显赫过。 虽然比不上先祖世代为楚将的项梁、项羽&#xff0c;但雍齿这个没落的世族后代&#xff0c;身上多多少少也还讲究点贵族遗风。 战国时期&#xff0c;以秦国的军功爵制为代表&#…

YOLOV8从环境部署(GPU版本)

一、安装&#xff43;&#xff55;&#xff44;&#xff41;和&#xff43;&#xff55;&#xff44;&#xff4e;&#xff4e; 1、安装cuda之前先打开英伟达控制面板查看自己的显卡信息 2、“帮助”—>“系统信息”—>“组件”&#xff0c;然后看第三行的信息“Nvidia …

Franz Electron + React 源码启动运行填坑指南

环境要求 安装miniconda python 环境electron/rebuild用得着&#xff0c;miniconda 默认自带的 python 是 3.11 版本&#xff0c;比较新&#xff1b; 安装virsual studio 2019 要把C桌面相关的都安装了&#xff0c;大概需要20G&#xff0c;不要安装到 C 盘&#xff0c;都安装到…

什么是Serverless ?

目录&#xff1a; 1、服务器发展阶段 2、Serverless定义 3、Serverless理解

AI图像生成-基本步骤

模型板块 1、新建采样器&#xff1a;新建节点-》采样器-》K采样器 2、拖动模型节点后放开&#xff0c;选择checkpoint加载器&#xff08;简易&#xff09;&#xff0c;模型新建成功 提示词板块 1、拖动正面条件节点后放开&#xff0c;选择CLIP文本编码器&#xff0c;模型新建…

《Fundamentals of Power Electronics》——转换器的传递函数

转换器的工程设计过程主要由以下几个主要步骤组成&#xff1a; 1. 定义了规范和其他设计目标。 2. 提出了一种电路。这是一个创造性的过程&#xff0c;利用了工程师的物理洞察力和经验。 3. 对电路进行了建模。组件和系统的其他部分适当建模&#xff0c;通常使用供应商提供的…

校园科普气象站的工作原理

TH-XQ3校园科普气象站是学校为了进行气象科普教育而设立的一种特殊设施。它不仅是一个能够实时监测和记录各种气象参数的气象站&#xff0c;更是一个促进学生对气象科学兴趣和理解的重要平台。 校园科普气象站通常包括一系列的气象观测设备和相关的科普设施。这些设备包括但不限…

【Git教程】(十八)拆分大项目 — 概述及使用要求,执行过程及其实现,替代解决方案 ~

Git教程 拆分大项目 1️⃣ 概述2️⃣ 使用要求3️⃣ 执行过程及其实现3.1 拆分模块版本库3.2 将拆分出的模块作为外部版本库集成 4️⃣ 替代解决方案 通常软件项目都是由单体小型系统开始的&#xff0c;在开发过程中项目规模和团队人员不断扩大&#xff0c; 将项目模块化会显得…

【保姆级】生成式网络模型基础知识(图像合成/语音合成/GPT)

生成式模型基础知识 初步接触生成任务 生成任务&#xff0c;顾名思义就是要去生成一个东西&#xff0c;比如生成图片/音频/文字等等。 大家接触最多比如chatGPT、stable diffusion、还有一些语音合成相关的东西。 那么问题来了&#xff0c;具体生成步骤是什么样的&#xff…