Linux相关概念和易错知识点(25)(信号原理、操作系统的原理、volatile)

Linux:详解进程信号(信号的捕捉流程,信号的阻塞、volatile关键字)(二) 文章目录1. 信号的捕捉流程1.1 捕捉流程 前言:阅读的时候如有不懂的的地方,可以看我之前写的详解进程信号(信号的种类、产生、注册、注销以及信号的各种处理方式)(一) 1. 信号的捕捉流程 在讲信号的捕捉流程的时候,我们需要首先明确的一个问题是:信号的注册,是否与操作系统所维护的进程PCB有关? 在上一篇文章中也讲了,信号的注册实质是将tast_struct(PCB)结构体中的struct sigpending结构体变量,它所包含的sigset_t signal位图中所对应的那个信号的比特位置为1,然后 阅读详情

目录

1.信号的产生

(1)kill

(2)raise、abort

2.对block、pending、handler表的管理

(1)信号集(sigset_t)

(2)block表的管理

①操作相关的函数

②sigprocmask

(3)pending表的管理

(4)handler表的管理

3.操作系统的原理

(1)硬件中断

①中断控制器

②中断号

③保护现场、恢复现场

(2)时钟源

①主频

②时间片

(3)异常处理

(4)软中断

①软中断和硬件中断

②系统调用表

③缺页中断

④异常、陷阱

(5)用户态和内核态(重点)

①用户区、内核区

②用户态和内核态之间的切换

③函数跳转

(6)捕捉信号流程

①用户态和内核态的切换

②对状态切换的理解

③对执行流概念的深化

(7)volatile

①可重入函数

②volatile、编译器优化

4.信号拓展

(1)SIGCHLD

(2)SIGALRM


1.信号的产生

(1)kill

信号究竟是如何产生的?我们已经知道信号的各种上层规则,但对于信号的来源还不是很熟悉。

看一下下面的代码,就能很快理解了。

kill本身就是个系统调用,而不仅仅是一个指令。int kill(pid_t pid, int sig);就可以实现对特定的pid进程发送sig信号,当信号发送成功时返回0,失败时返回-1。

也就是说所谓的kill指令,底层还是kill函数系统调用(指令->系统调用)。因此我们可以理解,当bash进程要kill掉其子进程时,就是调用的kill函数实现的。如当管道的读端关闭,系统会直接杀掉进程,就是使用的kill发送SIGPIPE信号

(2)raise、abort

int raise( int )意思是谁调用,就给自己发送这个信号,也就是说raise(9)可以杀掉自己。

void abort( void )意思是谁调用,就给自己发送SIGABRT 6号终止信号。相当于raise(6)

2.对block、pending、handler表的管理

在介绍了三张表的功能和调用流程之后,我们需要进一步讲讲如何修改这三张表,因为信号从接收到发送的全过程都由这三张表控制,管理这三张表本质上就是在管理信号的处理。

(1)信号集(sigset_t)

未决(pending)和阻塞(block)表都可用相同类型来存储,因为它们本质都是位图。这个位图的结构体是sigsei_t,也称为信号集,这个类型在两张表有不同含义:在pending表表示是否有接收到该信号,在block表表示是否阻塞该信号。阻塞(block)信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的屏蔽是指阻塞。

我们只需记住信号集的本质是位图,是未决(pending)和阻塞(block)表中位图的专属类型。 

(2)block表的管理

注意以下的函数都是针对block表的信号集!

①操作相关的函数

首先,我们需要自己创建一个sigset_t的变量,再对这个位图进行如下处理:

int sigemptyset(sigset_t *set);将位图清0; int sigfillset(sigset_t *set);将位图填为全1

int sigaddset(sigset_t *set, int signum);添加对signum号信号的屏蔽(本质就是修改位图,将对应位改为1);int sigdelset(sigset_t *set, int signum);删除对signum号信号的屏蔽

上述返回值都是0为成功,-1为错误

②sigprocmask

接下来我们要让我们的改动生效,上述所有的改动都是用户自己的修改,并没有写到内核中去。

int sigprocmask(int how, const sigset_t *newset, sigset_t *oldset);

how由如下宏定义决定功能:SIG_BLOCK增加传入的newset中状态为1的信号的阻塞;SIG_UNBLOCK解除传入的newset中状态为1的信号的阻塞;SIG_SETMASK直接用newset覆盖block表中的位图。注意这个newset就是刚才我们进行各种处理后的sigset_t变量(输入型参数)。而oldset是输出型参数,是修改前的位图,帮助我们恢复原来的位图。

除了上述用法之外,sigprocmask(0, NULL, &oldset)可以获取当前的block表

下面的代码,我利用阻塞表阻塞了2号信号的处理,使得信号无法被处理

(3)pending表的管理

int sigpending(sigset_t *set);用于获取pending表,set是一个输出型参数,该函数只提供内核中的pending表而不提供修改。其返回值0表示成功,-1表示失败。

int sigismember(const sigset_t *set, int signum); 判断signum对应信号是否有效。pending和block表都可以用,因为它们的位图的数据类型一致。我们需要手动传入sigset_t,这需要我们使用sigpromask、sigpending获取当前block、pending表。其返回值是1为真,0为假,-1为错误

(4)handler表的管理

signal函数可以进行信号捕捉,进而修改handler表,这里提一句即可。

int sigaction(int signum, const struct sigaction* newact, struct sigaction* oldact);

对于这个结构体,通常flag设置为0,sa_handler设置为指定的void(int)函数指针,使用为sigaction(2, &act, &oldact),即将信号2的默认处理方式替换为自定义处理,并且得到一个oldact用于备份。

struct sigaction的sa_mask成员还可以顺便帮我们添加要屏蔽的信号,我们自己设置sigset_t,在后续调用sigaction函数时,除了相应pending表被修改,block表也会加上sa_mask里面的几个屏蔽的信号。

3.操作系统的原理

要进一步理解信号,我们要对OS进一步深挖,来解释为什么我们键盘输入信号后OS能够及时的处理?是否需要OS一直等待键盘输入?同步异步是如何实现的?

(1)硬件中断

OS怎么知道键盘输入数据了?是否需要OS一直等待键盘输入?事实上,OS是启动的第一个软件,它不会轮询设备,设备数量太多了,键盘、磁盘、显示屏等,而是外设提醒OS后OS再来处理。

①中断控制器

以键盘输入为例,键盘按下会首先触发硬件中断(由硬件电路实现),这个硬件中断的信号不会直接传给CPU(物理上键盘并没有直连CPU),而是传给中断控制器(和每个设备直连),中断控制器再向CPU发送信息,进而被OS获取。

注意中断控制器可不会关心键盘按什么键,它只会告诉CPU发生了中断,CPU也不会关心,它收到中断后只会告诉OS有外设准备好了,之后在OS的管理下,键盘给CPU传信息,进而实现给OS传信息。至于键盘输入了什么,那是OS之后需要干的事。

再以磁盘为例,当磁盘想要给进程发信息时,虽然磁盘的寻址需要时间,要定位要准备,但这段时间OS不管,继续执行自己的任务,仅当磁盘准备好后中断控制器直接向CPU发送信号,执行读取操作即可。这样,硬件和OS实现了并行执行。

这里我们就会发现,信号和硬件中断有相似之处,信号是纯软件模拟中断的行为,硬件中断是靠软硬结合实现的(绝大部分是硬件)。

②中断号

对于中断控制器而言,每个设备都对应一个中断号,中断控制器利用中断号触发高电平告诉CPU谁中断了。这些中断号OS明白对应哪些设备,此时OS就回去调用对应的处理方法。要管理这些处理方法,OS有一个中断向量表IDT,这是一个函数指针数组,中断号就相当于数组下标。当中断控制器告诉OS中断号后,OS直接由IDT调用中断服务处理中断。这些中断服务包括读取硬盘、网卡等。方法内容由OS自定或安装驱动确定,同时我们也要知道中断号和中断处理方法是相对固定的,是软硬结合共同维护的。

③保护现场、恢复现场

在OS接收到中断控制器的信号,确定要处理中断之后,OS会将当前CPU各种寄存器的数据保存在中断的上下文,方便后续处理。保存好寄存器数据后,OS就会根据中断向量表找方法,执行中断处理例程(利用中断号查表)。处理完后就会恢复现场,继续执行任务。

OS通过中断实现了不主动轮询外设。所有外设(不含内存,它是存储器)都是这么处理的。

(2)时钟源

进程可以在OS指挥下调度执行,那么OS自己被谁指挥呢?时钟源。

①主频

我们已经知道了所有外设的信息是如何进入进程被处理的了,其中有一个外设,每隔一段极短时间就会给CPU发硬件中断,这个外设就是时钟源,它具有一个固定的中断号,以及一个固定的中断服务:进程调度。时钟源会一直推动OS进行进程调度,OS就是基于不断中断,调用中断向量表工作的。现在时钟源已经被集成在CPU内部,时钟源的触发频率就是CPU主频的概念(如14900K的6GHZ)。主频速度越快,处理任何操作都会更频繁。

OS在时钟的推动下自行调度,相当于说OS可以不做任何事,启动之后直接让自己进入死循环,利用时钟和其它外设触发中断推着它进行运行,执行方法。

总结:对于OS来说,只要完全没有进程,它就陷在死循环中,但只要有进程,就会一直忙着调度,这是时钟源的中断推着它进行的。我们因此可以说操作系统就是躺在中断中运行的。与此同时,OS也会自己去fork一些进程,这是内核固定进程,会定期去检查OS运行状态,定期把内核缓冲区数据进行刷新、检查闹钟等操作。 

②时间片

时钟中断是固定时间的,假设为1ns。进程的调度都设置了时间片,轮询着调度以尽量保持公平。OS要实现时间片,只需设置int count,当count == 1000(1微秒),每次时钟源中断触发进程调度都会让count--,count减到0后直接切换进程。

时间片就是在主频下的计数器,是以时钟中断为基础构建的。

(3)异常处理

我们已经知道,程序的崩溃就是靠信号终止的。

其中野指针触发段错误,操作系统直接用11号信号杀掉进程,我们捕捉信号后发现OS一直在触发信号。

同理,a / 0也会崩溃,会触发13号信号SIGFPE终止进程。

为什么OS知道我们的进程的内部出错了,为什么OS会一直触发信号?

以a / 0为例,CPU中有寄存器,a -> eax,0 -> ebx, 计算结果 -> ecx。同时还有个状态寄存器Eflags,有溢出标记位,默认为0,保证会把结果正常返回。但当溢出标记位为1,OS就知道CPU硬件内部出错了,OS找到对应的进程后就要用信号杀掉损坏CPU的进程。

当我们捕获信号之后,我们的进程没有退出,直到时间片到了,进行进程切换时会OS会保存寄存器数据,以便下次调用恢复。其中Eflags的标记位也被保存了,OS不会修改它。由此以来,每当调度到这个进程时,CPU都会显示异常,OS会一直尝试杀我们的进程,这导致了死循环调用信号处理。

同理,OS怎么判断野指针呢?CR3寄存器保存页表起始地址,虚拟 -> 物理地址的转换是MMU内存管理单元操作的,它集成在CPU中。当访问野指针(虚拟地址)时,MMU转换去读0,发现无法访问,于是触发了硬件错误,OS知道后发送信号尝试杀掉进程。当后续再轮换调用时,MMU的错误被完整地继承了下来,所以OS会循环发送信号。

(4)软中断

①软中断和硬件中断

上述都是外部硬件中断,需要硬件设备触发。有没有仅靠软件进行中断的呢?

为了让OS支持系统调用,CPU专门设计了对应的汇编指令(int或者syscall),在没有外设下,可以让CPU内部触发中断逻辑。这些汇编指令就可以写在软件中,通过汇编指令 + 中断号推动CPU执行中断向量表方法,这就叫软中断。硬件中断和软中断仅仅是触发方式变为汇编调用,其余流程一致,这很好理解。

两种触发方式最终都是利用中断号调用中断向量表中的函数。例如,int N 指令会触发中断号为 N 的软中断,操作系统会根据该中断号来找到对应的处理函数(范围从 0x00 到 0xFF(即从 0 到 255))

②系统调用表

系统调用也是通过中断完成的。OS系统调用都在系统调用表,集中管理,是由“下标”调用的。系统调用表特别底层且固定,用户无法查看这个表的存在。这个表中各个函数的下标叫系统调用号,在中断向量表中设置一个系统调用的入口函数,用系统调用号调用。

要调用系统调用,就要想办法软中断,int 0x080就是软中断到执行系统调用入口函数,进入固定例程。系统调用号会提前写到寄存器中,可以直接被获取。

系统调用的过程:先把要调用的系统调用号用寄存器存起来,再触发软中断陷入内核,OS根据中断向量表开始进入中断服务,根据寄存器里的系统调用号自动查系统调用表,执行对应方法。

OS提供的系统调用接口根本不是C函数,而是系统调用号 + 约定传递的参数,通过触发软中断、进行中断服务调用的方式实现系统调用功能的。系统调用的真正的底层实际上是汇编。

GNU、glibc给系统进行C语言封装,给我们提供C语言的系统调用。所以我们用的所有系统调用都是C的,是它对该平台的系统调用进行了统一的上层封装,这也是C具有跨平台性的本质。C语言的上层封装还保证了我们系统调用的安全性,不会导致对系统造成伤害。

③缺页中断

缺页异常意思就是有虚拟地址,但物理内存没有申请(分批加载)。当OS需要用到相应空间,但发现还没有物理内存时,就会触发中断,申请并加载物理内存。

④异常、陷阱

OS就是躺在中断例程上的代码块,几乎所有操作都会转为中断。缺页中断、内存碎片处理、除零、野指针错误都是转为软中断,OS会设置中断号走中断例程。异常、陷阱都会转为软中断来处理。

如果发生了软中断且不是因为出错,就是单纯为了陷入中断(系统调用),这叫做陷阱。

而像除零、野指针这种触发错误导致软中断的叫做异常。

OS会根据不同错误或者陷阱由中断号进行调用。

(5)用户态和内核态(重点)

①用户区、内核区

在32位操作系统下,进程的地址空间划分了4G,其中这块虚拟的地址空间包含了我们已知的堆栈段、代码段、数据段、以及命令行参数列表等。所有的这一切所在的内存区域都属于用户区。用户区从0G ~ 3G,共3G的大小。

3G ~ 4G这块叫做内核区,这块内核区也是虚拟的,它拥有共同的内核页表,同用户页表一样,都是映射到物理内存中的。不过需要注意的是,不同进程的内核区都会映射到同一块物理内存。

这里需要注意的是,用户页表每个进程一份,因为虚拟地址和物理地址都要根据不同进程实际情况映射。;而内核页表则是整个系统一份,无论是虚拟地址还是映射的物理地址都是同一份。对于任何进程来说,无论如何调度,它们都能找到同一个OS,访问同一张中断向量表和系统调用表。

无论调用任何函数(库、系统调用),都是在我们自己用户区进行调用的(代码段在用户区)。而被调用的系统调用方法的执行是在内核区进行的。

本质上说,内核区映射到同一块空间是因为OS只有一个,那么如果使得进程映射多块空间,就相当于启动了两个系统,这就是内核虚拟机的思路,当然还有用户虚拟机,这里提一句。

②用户态和内核态之间的切换

前面我们已经知道用户区和内核区了,这两个区域只能被具有对应的身份的人访问,标记身份的就是用户态和内核态。

如果我们要进入内核区,我们需要将我们的身份进行改变,即用户态 -> 内核态;同理,如果要访问用户区,我们也应该将内核态 -> 用户态。 

标记用户态和内核态的是CPU的CS段寄存器。它有两个bit位的标志(CPL):00表示内核,11表示用户。修改用户内核态的本质就是修改标志位。我们都知道MMU是集成在CPU内部,负责进行虚拟地址 -> 物理地址的,只要CPL不允许访问,MMU也自然不会给我们转换地址,我们也自然访问不了对应的空间。用户态和内核态的权限管理是硬件层面的。

当我们在用户区调用系统调用时,会使用int (中断号) 和 syscall (中断号) 触发软中断,进到中断向量表里面去执行系统调用入口函数,进而访问系统调用表,执行系统调用方法,最后回到调用处继续执行代码。在整个过程中,当触发软中断时就会第一次切换状态,将权限标志位CPL改为0,进入内核态。执行完后返回调用处又会切换一次,CPL改为3,进入用户态。

③函数跳转

在上述流程中,我们会有疑问:函数是如何跳转的,跳转回来时如何找到最开始的地址的?A函数调用B,A会把调用B的下一个地址先入栈,后面出栈后就能够找到下一句地址。由此以来就能正确地在内核区和用户区之间进行跳转。汇编层面所有操作都是基于寄存器、地址的。

(6)捕捉信号流程

有了前面知识的积累,我们能够更底层地理解信号捕捉的流程了。

①用户态和内核态的切换

如果来了一个信号,有可能当前进程正在做更重要的事,我们要把信号保存到pending表中。当进程从内核态切换回用户态的时候,进程就会进行信号检查do_signal(),检测当前的pending和block表决定是否处理信号。

当要处理信号时,会根据不同处理信号的方式决定走向。如果是DFL和IGN的情况,那么进程会不急着返回用户态,会在内核态把方法执行完成后再返回(DFL和IGN的代码都在内核区);如果我们自定义捕捉了信号,这些代码都在用户区保存,所以我们要切换回用户态执行处理代码。处理之后,会回到内核态,执行sys_sigreturn()返回主程序,回到用户态。

②对状态切换的理解

为什么执行自定义函数时要做权限切换?直接用内核态执行不行吗?自定义函数存在用户区,需要用户态,而内核态权限更高,切换回用户态是为了避免安全风险,防止内核态被利用。同时内核态进入用户态后执行自定义函数,意味着用户态的操作后果自负!

③对执行流概念的深化

我们执行完自定义函数后,想要回到主程序。为什么不直接回去,反而还要进入内核态呢?实际上自定义函数和主程序没有任何关系,在程序中不存在任何调用关系,信号处理和主程序是两个完全不同的执行流,因此只能先返回到内核。当触发硬件中断,OS会保护现场,存储各个寄存器的状态。其中pc寄存器就保存下一条指令的地址,在这里就是主执行流的下一条指令的地址。因此回到用户态,需要在内核态调用sys_sigreturn()函数,恢复上下文,才可以回到初始时的在主程序执行流。

到这里,我们也能彻底理解信号处理没有新开一个进程,主执行流和信号捕捉执行流,信号捕捉执行流没有在主执行流中被调用,而只是一个主执行流中的分支,进程调用过程中两个分支不会冲突。

下面是执行流切换的过程,仔细体会

(7)volatile

①可重入函数

若一个函数被两个以上执行流同时进入,就有可能遇到下面这种情况,这是单执行流的情况下遇不到的。


因此实例的insert函数是不可重入函数。相对的,还有可重入函数,也就是可以被被两个以上执行流同时进入同时不发生错误的函数。怎么判断呢?

只要使用了全局资源的,new了空间的,基本都是不可重入的,使用的都是局部变量的就可能是可重入的。基本上STL都不可重入,大部分函数也都不可重入。但也有专门设计的系统接口带_r,表示可重入。

②volatile、编译器优化

gcc有优化选项 -O0基础优化,-O1,-O2,-O3优化级别依次增加。

对于主程序执行流和信号处理执行流来说,由于它们从语法上没有任何联系,但实际上可以通过全局资源联系,编译时就有可能被优化出bug。

若在主执行流中对一个全局变量没有修改,而实际上这个全局变量在信号捕捉执行流中被处理。对于高优化的编译器来说,这个全局变量在信号捕捉执行流中的处理无效。我们要从底层来理解优化。

当编译器优化很大时,一些主执行流没有修改的变量会变成寄存器变量,这个变量永远无法被修改。当我们使用这个变量来进行判断时,逻辑判断会直接用寄存器里面的值进行判断而不会使用内存的数据,这就导致信号捕捉执行流中修改变量不会生效,寄存器 + 优化屏蔽了内存的可见性。

如果担心被优化导致出现bug,我们可以使用volatile关键字修饰全局变量,volatitl int flag = 0;就不会被寄存器屏蔽了,这个关键字相当于告诉编译器不要对该变量进行任何优化,以保持内存可见性。

4.信号拓展

(1)SIGCHLD

父进程一般关心子进程什么时候死,这样好回收,以免出现孤儿或者僵尸。事实上,子进程退出时都会向父进程发送SIGCHLD信号,告诉父进程自己结束了。不过默认情况下SIGCHLD的Action是Ign(忽略)。我们可以手动捕获这个信号,当父进程收到信号时再去wait,这样就能实现父子进程异步执行,而不是让父进程一直阻塞在原地。

但是有个问题,即信号处理过程中最多允许再接收一个信号,后续的没意义(pending已经为1了),所以每次需要waitpid + WNOHANG循环,用返回值来判断是没等完还是等完了。

仅Linux下,signal(SIGCHLD, SIG_IGN)处理后,这样fork出来的子进程在终止时会自动清理掉,没有僵尸,不过父进程也得不到status。这里可认为是Linux的特殊处理,仅仅用来处理僵尸的情况。

(2)SIGALRM

alarm(seconds)函数可以设置一个一次性闹钟信号(只会响一次,重复使用需要多次调用),闹钟时间到了之后会发送一个14号SIGALRM信号,默认就是Term终止。我们可以捕获它,利用alarm写一个统计服务器1s执行某种操作多少次的代码,可以用alarm来对比IO对效率的影响有多大等操作。

alarm(0)取消闹钟,其返回值是闹钟剩余时间。如果闹钟自己响了,那返回值就是0。

【Linux】进程信号详解 介绍了信号的种类、属性、信号的产生、信号在内核中的存储形式、信号的捕捉、信号的处理流程等 阅读详情

相关推荐

SAP COPA实战:手把手教你用CMOD增强搞定自定义特性派生(以客户字段为例)

本文详细介绍了在SAP COPA模块中通过CMOD增强实现自定义特性派生的全流程,以客户字段为例,展示了从特性设计、SPRO配置到特性派生逻辑实现的完整步骤。重点讲解了如何利用CMOD增强技术解决业务需求,提升获利能力段分析的灵活性和准确性。

weixin_30275415的博客 428

Linux 进程信号(超详细的都在这里了,从多个方面【信号的产生(系统闹钟)-信号的保存(sigset_t)-信号的捕捉-基于信号操作系统的运行-可重入函数-Volatile-SIGCHLD信号】讲解

在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产⽣机制。这些条件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据产⽣的SIGPIPE信号)等。当这些软件条件满⾜时,操作系统会向相关进程发送相应的信号,以通知进程进⾏相应的处理。简⽽⾔之,软件条件是因操作系统内部或外部软件操作⽽触发的信号产⽣。系统闹钟,其实本质是OS必须⾃⾝具有定时功能,并能让⽤⼾设置这种定时功能,才可能实现闹钟这样的技术。定时闹钟也要被管理的:先描述,再组织。

m0_73523775的博客 4353

大麦网回流票监控实战:如何利用sign参数和cookie实现自动抢票(避坑指南)

本文详细介绍了如何利用sign参数和cookie构建大麦网回流票监控系统,实现自动化抢票。通过分析大麦网票务系统的核心机制、获取关键身份凭证、逆向分析sign签名算法,并构建完整的监控系统,帮助用户高效捕捉回流票机会。文章还提供了实战避坑指南和合法合规的注意事项,确保系统稳定运行且符合平台规则。

weixin_28568819的博客 411

系统级基础信号知识【Linux】【信号产生,捕捉,原理】

操作系统中的信号是一种在进程间传递信息和通知的机制。它可以用来通知进程发生了某种事件,比如用户按下了某个键盘按键、进程收到了某个信号或者发生了某个错误等。手机收到新短信或来电时会发出提示音,这就是一种信号,通知用户有新的事件发生。交通信号灯会发出红、黄、绿三种不同的信号,指示车辆和行人何时可以通行。火灾报警器发出警报声,通知人们有火灾发生,需要立即疏散。门铃响起,通知主人有人来访。警报器在发现入侵者时会发出警报声,通知屋主有危险。可选的处理动作有以下三种1. 忽略此信号。

菜菜成长为后端开发大佬 2724

文档信息抽取技术:从非结构化文本到结构化信息的旅程

文档信息抽取技术是一种将非结构化文本转化为结构化信息的技术。这种技术可以从各类文档中自动提取出如实体、关系和其他重要信息,并将它们转化为方便计算机进一步处理和分析的格式。

INTSIG的博客 980

从零开始:如何用Python建立你的第一个人工智能模型

在这篇文章中,我们将介绍如何从零开始使用Python建立你的第一个人工智能模型。无论你是刚接触编程的新手,还是有经验的开发者想进一步探索人工智能领域,这篇文章都将为你提供清晰、详细的指南。我们将一步步探索数据预处理、模型建立、训练和测试的过程,以及如何解读模型的结果。

Allen1862105的博客 4万+

操作系统 : Linux进程信号

理解信号的工作原理对于深入掌握Linux系统编程至关重要。信号的处理涉及多个层面:从信号的产生、保存到最终的捕捉与处理,每一步都体现了操作系统对进程管理的精巧设计。本文将系统性地介绍Linux信号机制,涵盖以下核心内容:信号的基本概念:信号是什么?它是如何工作的?信号的产生方式:从终端按键到系统调用,多种触发信号的途径。信号的保存与阻塞:内核如何管理未决信号?信号屏蔽字的作用。信号的捕捉流程:用户态与内核态的切换,信号处理函数的执行机制。高级话题:可重入函数、volatile关键字、SIGCHLD信号的应用

m0_74951730的博客 1246

机器人常用坐标系详解:原理、获取方法及实战实例

本文系统阐述了机器人四大核心坐标系(世界、基、工具、工件)的原理与标定方法。世界坐标系提供全局参考基准,基坐标系绑定机器人本体,工具坐标系定义末端执行器位置,工件坐标系则锁定作业对象位置。通过齐次变换矩阵实现各坐标系间的关联转换。重点介绍了三类标定技术:手动设定法(世界坐标系)、三点/六点法(工具TCP标定)和视觉识别法(工件标定),并以工业机械臂上下料为例演示了完整应用流程。随着技术发展,自动化标定、动态坐标系等新趋势将进一步提升机器人作业精度与效率。

start_up_go的博客 513

Linux信号详解

目录前言一、信号概念1、生活中的信号2、计算机中的信号 3、实现信号捕捉二、产生信号的方式1. 通过终端按键产生信号格外拓展:核心转储(core dump)status 2、调用系统函数向进程发信号3. 由软件条件产生信号 4、硬件异常产生信号三、阻塞信号1、概念 2、内核中的表示3、信号集操作函数3.1用户层函数3.2 系统接口四、信号捕获1、用户态和内核态 2、内核如何捕获进程信号2.1捕获信号过程2.2 sigaction五、函数的重入六、c语言关键字volatile总结哈喽,小伙伴们大家好,本篇文章

weixin_59371851的博客 5072

基于mengzi-t5训练生成式模型

【代码】基于mengzi-t5训练生成式模型。

开卷有益 170

APM32取代STM32的经验分享

我仔细检查了代码中与硬件相关的部分,例如外设寄存器的访问和中断处理程序的设置,并进行了相应的修改。在本文中,我将分享我将APM32引入项目中取代STM32的经验,并提供相应的源代码。我查看了APM32的官方网站、开发者论坛和相关社交媒体,了解了开发者社区的活跃度和可用的资源。总结起来,将APM32替换STM32的过程需要经过几个关键步骤:了解APM32的规格和功能、评估软件生态系统、迁移代码和调试,以及性能和功耗优化。在开始替换之前,我对APM32进行了详细的研究和评估,以确保它能够满足项目的需求。

UplKubernetes的博客 2004

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

创建子进程时,只创建PCB,创建出来的PCB继续指向父进程的地址空间代码区假设有很多函数存在,让不同的PCB执行不同的函数相当于在一个进程内部包含多个执行流,指向同一个进程内的不同代码区域每个PCB都是单独的线程线程在地址空间内运行,所以该线程属于进程。

qq_62939852的博客 2250

财务数智化——解读224页 2025年数智技术业财融合应用蓝皮书【附全文阅读】

《数智技术赋能业财融合蓝皮书》面向企业管理者及数字化转型从业者,系统阐述数智技术在业财融合中的应用。全书围绕技术基础(定义、分类、发展趋势)、融合框架(三阶段路径与三层价值)、七大行业应用场景(涵盖核算自动化、智能收款、项目管控等)、关键技术(AI、大数据等)及实施要点(组织协同、数据治理等),通过典型案例为企业提供从技术到实践的全面指导,助力实现数据贯通与价值创造。

cdfunlove的博客 1747

Linux 进程信号:内核中信号结构、阻塞信号、捕捉信号

Linux 进程信号:内核中信号结构、阻塞信号、捕捉信号

Han同学 1865

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

linux信号相关概念

信号、信号操作函数

羊羊羊 866

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【Linux系统编程:信号】产生信号 | 阻塞信号 | 处理信号 | 可重入函数 | volatile | SIGCHLD

通过学习信号可以理解进程与进程的一个相对关系,还能理解操作系统与进程的关系。要注意的是进程间通信中的信号量与这里的信号没有半毛钱关系,就像老婆和老婆饼。掌握 Linux 信号的基本概念掌握信号产生的一般方式理解信号递达和阻塞的概念,原理掌握信号捕捉的一般方式重新了解可重入函数的概念了解竞态条件的情景和处理方式了解SIGCHLD信号, 重新编写信号处理函数的一般处理机制。

DanceBit的博客 1157

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

【linux系统编程】信号——信号保存+信号处理

这是一篇将近3w字的超长文章,是信号三部曲的第二部曲及第三部曲的集合,并且还包含以前没学过信号不能讲的知识这里都进行了补充。首先讲了信号保存,信号是如何保存的。然后讲了信号处理,以前说过信号是在合适的时候被处理,什么合适的时候?进程从内核态返回用户态的时候,那什么是用户态,内核态,进程什么时候去OS中执行代码的,进而将虚拟地址空间在做进一步的阐述。以及有关信号的一些接口和接口的使用,并且还阐述如果同一时间发送同类型的信号OS会做什么处理等等知识,以及可重入函数volatile关键字SIGCHLD信号相关知识

fight_p的博客 2104

【Linux】进程信号

进程信号

VHhhbb的博客 1773

Linux操作系统--阻塞信号、捕捉信号

文章摘要:本文详细介绍了Linux系统中信号处理的相关概念和机制。主要内容包括:1)信号递达与未决状态的区别,阻塞信号和忽略信号的不同;2)信号在内核中的表示方式,包括阻塞标志位、未决标志位和处理函数指针;3)信号集操作函数和信号屏蔽字的设置方法;4)信号捕捉的实现原理和sigaction函数的使用;5)可重入函数的概念及其重要性;6)volatile关键字在信号处理中的作用,防止编译器优化导致的数据不一致问题。文章通过具体示例和函数原型,全面阐述了信号处理机制的关键技术点。

2301_78702440的博客 1107

【linux】进程信号(三)信号的处理,信号的捕捉,sigaction,可重入函数,volatile,SIGCHLD信号

一、浅度理解信号的处理 二、重谈地址空间3 三、理解操作系统 四、深度理解信号的处理 五、信号的捕捉,sigaction 六、可重入函数 七、volatile 八、SIGCHLD信号

重铸自身根基的博客 1281

linux信号的概念

信号产生的方式:无论信号如何产生最终一定是谁发送给进程的?OS!为什么?OS是进程的管理者!

Qianxueban的博客 1131

深入剖析Linux——进程信号

Linux信号深度剖析

终为nullptr 2099

[Linux]信号底层概念与操作

信号是操作系统里进程间通信的一种方式。其本质是传递事件通知,让进程知晓特定情况。信号产生多样,键盘输入触发硬件中断后产生,异常、软件条件达成以及系统调用也能引发。产生后,进程保存信号并通过设置处理函数来应对,利用信号屏蔽字控制处理与否。在底层,涉及内核态与用户态切换,内核空间负责处理流程。信号捕捉处理函数完成具体操作,像SIGCHLD信号用于告知子进程状态改变,处理时还得关注可重入函数及volatile关键字的运用呢。

m0_74207969的博客 1279

【Linux信号】Linux进程信号(下):可重入函数、Volatile关键字、SIGCHLD信号

volatile的字面意思是“易变的”。这个变量可能会在当前代码流之外被修改(例如:由信号处理函数、多线程、或者硬件中断修改)。因此,编译器在编译时严禁对该变量进行任何“假定不变”的优化,每次使用它时必须老老实实地去内存地址中读取。下面我们演示如何使用gcc -S命令来查看开启优化后,有无volatile关键字在生成的真实汇编代码上的具体差异。我们直接通过对比真实的汇编逻辑来看一下volatile是如何“保命”的。// 或者去掉 volatilewhile (!flag);

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 1136

【Linux 17】进程信号

1. 进程的运行。2. 信号的产生。3.信号的捕捉。4. 信号的阻塞 (保存)

shangguanxiu的博客 2395
上一篇: Linux相关概念和易错知识点(24)(认识信号、信号捕捉)
下一篇: Linux相关概念和易错知识点(26)(命名管道、共享内存)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值