24. linux系统基础

 两个进程间想通讯,必须要通过内核,今天讲的信号其实本质也是讲进程间通讯的意思,那么你为什么可以在shell环境下,可以和一个进程发kill-9啊?
shell是不是相当于一个进程?你自己运行的那个进程是不是也相当于一个进程?这样的话,是不是一个进程给另外一个进程发送信号啊?他本质上也是进程间通讯,也是需要通过内核,

作业,在父子进程间执行,psux的命令啊?
在兄弟进程间,完成这么一个功能,同时增加别的功能
父进程里面是不是需要回收啊?回收子进程,
这个代码你看一下你到底哪里没写出来

 

 

我们完成的功能是psux groupby里面的命令吧?
所以说咱们应该是在创建子进程之前应该创建一个管道啊?这样的话,fork出这两个子进程以后呢?这两个子进程是不是都可以使用这两个管道了啊?
实际上在使用管道的时候呢,是不是就使用了两个文件描述符啊?我们操作管道是不是其实就是操作两个文件描述符的?

在循环里面创建了两个子进程,在36为什么加break?
为了让子进程不再去fork子进程,
循环创建两个子进程,

 

 

 

 

这个wpid>0 这个是不是表示子进程已经死掉了,至少死掉了一个,至少你可以调用这个宏打印一下他的退出状态,65--71

父进程做的事情就是这么些个,这一块咱们前面是不是写过两三次了?

第一个子进程 i==0 79 我们的目的是让他往管道里面写吧?他执行ps aux 这个命令 这一个和接下来的这一块,是不是和咱们前面讲的那个图是一模一样的?只不过是把那个父进程变成子进程吧?
所以说他用哪端就留哪端,不用哪端就关闭哪端,在这他用的是管道的写端,所以他关闭的是管道的读端,

他执行的dup2这个函数 是不是可以实现文件重定向啊?文件重定向和文件描述符的复制,本身上是不是一个意思啊?两个说法而已

那么他要将谁重定向谁?是不是应该是把标准输出重定向到管道的写端啊?所以这一步操作就是干这一个事情的,这一步操作是不是前面咱们说过两三次了?还记的将标准输出重定向到文件当中去吗?那个只不过fd[1]是一个open打开的一个文件啊?在这他是个管道 本质上是不是一样的啊?
在这执行了一个命令 execlp这个命令 86,这个命令内核当中所做的操作是不是其实是将ps这个命令他的代码端,是不是替换掉了,当前这个子进程的代码段啊?如果说这个函数执行成功,后面perror这个函数还能执行吗 87 89?不能执行了,其实 如果成功 后面这个相当于废话,冗余代码 后面可以没有,那是不是不用写了?因为他有可能失败,那你比如说你这个ps你写错了,或者你的参数不对,都有可能会报错,出于习惯你把这个写上 89
万一报错的话,你是不是可以看到啊?如果你不写你就不知道了,
看第二个子进程,i==1 第二个子进程 很显然是不是他要执行管道右边的group bash的命令啊?
所以说在这呢也应该是做重定向操作吧?当然用哪端留哪端,不用哪端关哪端,在这他是不是用读端?所以他关掉了写端,接下来是不是和咱们前面讲过的一模一样了?
然后他也做了一个重定向,后面来执行一个命令,这个execlp一定要会用100 
这个函数其实execlp还可以被这个execl函数代替啊?
如果你代替之后,那么你第一个参数是不是要加路径了?怎么查看这个命令的路径啊 which 
你这个用命令查的话,是不是只能查这个系统命令的这个路径,你能查这个普通用户写的这个应用程序的路径吗?查不了了,

这个代码应该说有一定的综合性,在这里咱们综合用到了 创建管道, fork  循环创建子进程的时候需要你注意的事项,

父进程回收子进程 waitpid waitpid 函数的使用,务必要搞清楚

还有这几个状态的宏65 71 这几个状态的宏你记不住,你就查,man  waitpid 
文件重定向操作,在一个子进程里面执行另一个命令,另一个系统命令,或者是应用程序的函数,execl的函数,
综合应用了这些东西,应该是这个代码应该说这个如果大家掌握了咱们前面讲过的那个父子进程间完成ps aux 这样的一个命令的话呢,那么我想在兄弟之间完成也不难,而且咱们这个wpid是不是前面也学过了?如何让父进程回收子进程?大致也会了,其实就是两段代码一组合就可以了?

看懂代码之后,看看是否符合自己的需求,不符合的话,拿过来大概改一改,就可以用了,一般就这么用的,很少会让你从零开始写,即便是从零开始写,我想的话,应该是会有大概的模板,比如说这个代码和这个代码类似,你照着他写就可以了,一般这么来写,如果说研发一个产品的话,从零开始写,那么周期会很长,

他能够用现实的东西,比如说库函数,现成的类等等,他就用现成的,

共享映射区,他的实现原理,将文件内容映射到共享映射区,那么这样的话,你再操作这块内存是不是就相当于操作这个文件啊?那么我们这个共享映射区其实就是一共有两个类,一个是MAP_SHARED 一个是MAP_PRIVATE那其中MAP_SHARED是不是能够将修改内存的数据反应到文件当中去?但是MAP_PRIVATE就不行了,一般时候用的话,就用MAP_SHARED就可以了,
这个关于mmap函数的用法,咱们就不过多的说了,那么这个函数呢,虽然说参数比较多,但是很多参数都是有固定写法的,那么唯一有那么一两个让你准备的 是不是就那个文件描述符了?还有文件大小,其他的要你准备吗?都是现成的,直接填就可以了,mmap他不但能够完成父子进程间,或者兄弟进程间,也就是有血缘关系的进程间通讯,是不是也能够完成没有任何关系的两个进程间通讯啊?当然你要想让他完成任何两个进程通讯的话,必须有一个文件,
还有一个匿名映射,大家了解一下就可以了,匿名映射是不是只能运用于有血缘关系的进程间通讯啊?因为他没有文件,如果两个毫不相干的进程,他就没有联系的纽带,

那么关于这个fifo 是不是更简单了?fifo是不是你得用一个文件啊?管道文件,这个管道文件是不是咱们操作系统里面的系统文件类型是一样,第一个字母是p 那么这样的话,必须是文件,那么两个进程通过打开一个共同的文件来操作这个管道,
基本上复习的文件就这么多

熟练使用信号捕捉函数 sigaction 重点掌握

熟练使用信号捕捉函数 sigrtal 这个不能跨平台移植,只能在linux上使用,在其他系统中呢,他表现的行为有所区别,
sigaction都是一样的,可移植的
你在linux写这个用这个,那么你拿到unixs上不做任何修改,也可以,但是用sigrtal这个的话就不一定了

熟练掌握使用信号完成子进程的回收  是本章的重点,

前面讲回收,是不是父进程在一个循环里面去循环回收啊?那么循环回收还能做其他的事情吗?是不是做不了啊?这儿就可以了

也就是说有子进程

有子进程退出,他才去回收,没有子进程退出他不管,有点类似于 你在网上买的货,你是不是要在家等着货过来呀,你不可能一会往那跑一趟 一会往那跑一趟去问吧,人家打电话你去拿就可以了 最终是不是异步处理啊?,你并不知道你的货什么时候到,对于这个来说,你并不知道你的子进程什么时候死,

 

信号是信息的载体, 在c语言当中,结构体是不是也是信息的载体?
Linux/UNIX 环境下,古老、经典的通信方式,这个信号已经出了很长时间了,他不是一个新的技术,早就有了,现在依然是主要的通信手段
这个信号仍然是两个进程通信的主要手段之一

 

信号的优先级高,
不管程序执行了什么位置,只要是有信号发生了,优先处理信号,信号处理完以后呢,再回过头来再执行,刚才是从哪中断的从哪执行

信号是软件层面的中断,被称为软中断

程序一开始运行在用户区,运行的时候有的时候需要接触到内核啊?下面是内核区,
进程a要想给进程b发送一个信号,首先他其实进程a是发给了内核,然后由内核再给你转发给进程B  进程a先发给内核,然后内核再转给b 那么内核怎么知道转给b 你发信号的时候你kill 你会写一个 当然第一个肯定要写一个哪个信号吧?要发哪个信号 给哪个进程发哪个信号,是不是至少两个信息啊?一个是进程pid 一个是进程的编号吧,信号的编号?

所以说你指定了进程的pid了,那么这样的话,其实是你先告诉内核的吧?你这个进程a kill的时候,你已经告诉内核了,你要给谁发送信号,那么内核是不是就知道给谁转发了?所以说 他知道转给b 是不是返回来也是一样的?把进程b给进程a是不是也是这样的?

比如说我这个里面,是我们这个方块是我们的代码,执行到这个位置了,突然发生了信号,他是不是优先处理这个信号啊?这样处理信号,在这我们画一个,我们写的处理信号的动作是执行一个函数, 这个函数是我们自己写的,用的是回调函数,这个信号处理函数,  
也就是我们在处理信号的意思
这个信号处理完了,回到哪去呢?从哪中断回到哪。举个例子 比如说你这个信号执行到第十行了,突然产生信号了,那么接下来是不是要处理这个信号呢?处理完这个信号接下来是不是又回到10行?从这个图上我们可以看得出来,信号的优先级 要高于其他动作啊

出现信号要优先处理信号,

处理完信号以后呢,再去执行刚刚你从中断的位置上,再继续往下执行,高于普通操作,信号有三种状态,一个是信号产生,这个信号是怎么产生的,这个信号是不是得产生啊?
你这个程序不close掉他会产生信号吗?

sleep 100现在是不是已经执行起来了?
是不是你查的时候man 1 是不是你查的命令啊? 你不写他从1开始发
man 2 查的是系统调用 或者是系统函数

man 3 查的是c语言的库函数

man 7今天会讲

定时器 alarm,每一个进程都有一个唯一的定时器,这个定时器有点类似于手机上的闹钟,

非法访问内存(段错误)会close掉,
除0,你的进程会立刻死掉

命令产生kill


未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态
一个进程收到信号,默认会去处理它,但是如果这个信号被阻塞了,阻塞的意思是不是暂时不让他处理的意思? 他就相当于屏蔽掉他了,这时候就相当于处于未决状态

这个是后面讲 信号级相关操作的时候会讲这个,

递达:递送并且到达进程。 信号已经处理了 就是递达状态

 信号的处理方式 信号的处理动作
有三个 
直接看手册

man 7 signal 这个里面就能够把这个信号相关的信息传达出来,比如说signa1 dispositions 信号的处理方式,term是不是终止的意思?终止进程

Ign 忽略信号

Core内存非法访问照成的

stop进程暂停

Cont 使进程继续执行

stop 和Cont 他俩是一对, 暂厅停止是一对


信号的四要素,每一个信号 用四种属性去描述他
一个是信号的名字SIGHUP  大写的宏叫信号的名字,
SIGINT 是不是刚刚说过了,ctrl+c 是不是可以产生这个信号啊?

value指的是信号的编号

action 信号的默认处理动作,什么叫默认处理动作啊? 那么对象如果你的进程不处理他,他有一个默认处理动作

后面咱们会讲怎么捕获他,
comment 这个信号怎么产生的,一个信号描述,
被键盘中断,其实就是说的ctrl+c


SIGPIPE 往一个没有读端的管道写数据,你把他的管道读端给关掉,然后你可以写数据,你看看会不会收到这个信号,一定会收到这个信号,

SIGCHLD重点讲的这个信号,Ign 忽略,什么情况下才会产生这个信号?是不是子进程 停止或者终止

停止相当于暂停的意思,stopped 和terminated是不一样的,terminated是终止的意思,进程彻底死掉了

The signals SIGKILL
and SIGSTOP cannot
caught,b1ocked, or ignored
不能阻塞 不能忽略,

收到这两个信号SIGKILL  SIGSTOP 必须有处理动作,这两个处理动作一定是默认动作,不能定义,只能是默认动作吧?

SIGKILL的默认动作是什么呢?Kill signal默认是终止的意思,Term


SIGSTOP stop 是暂停的意思

在这里面信号里面有3种值,

这个信号的编号,有的是有三种值,SIGUSR1 SIGUSR2 
在一个系统上只有1个值,在linux系统下他是终端这个值,其中你不知道这个也没有关系,因为你在使用信号的时候呢,你不要使用编号,你使用宏名就可以了,那么宏名你管他是什么,不同的系统上他表现的数值不一样,对于这个宏是不是都一样的?

别的信号用的相对比较少,SIGIOT SIGEMT
SIGSTKFLT ... 你也不用再关注了 很少用它

其实信号的处理动作还有两个,是暂停和继续执行,大部分都是终止,终止是不是才会对你的系统照成的影响最小啊? 你比如说你这个你本来进程被core掉了,你如果不知道怎么终止的话,他是不是会非法访问其他内存啊?有可能给你修改吧?这一修改不得了,其他程序也会core掉了,是不是会照成连锁反应啊?

忽略 丢弃不处理的意思,相当于就当他没发生

捕捉信号 捕捉是咱们需要注册shell命令函数,让他去执行我们自己定义的函数,

信号有很强的延时性,信号处理是不是要进入 内核啊,我们信号是不是需要暂时的阻塞啊?那么阻塞 如果说后面呢你不阻塞,再写出注册,这个信号是能够被处理的,从这点来说,我们可以知道,这个信号是有延时的,我们介绍信号集相关函数,我给你介绍阻塞这一块你就知道了,一开始你不处理他,后来呢,你又想处理他了,然后解除注册,这个信号就能够被处理了,从这一点来说也能够感觉到这个信号是有延时性的

然后你去不阻塞他的话呢,他的这个延时你是感觉不到的,因为这个时间十分的短暂,这个信号处理的话呢,他会进入到内核,
咱们前面讲到pcb的时候给大家说过了,这个信号集相关的东西是不是都在这个pcb里面,在这里面主要指的是阻塞信号集和未决信号集。

 

信号相关的函数, 先给大家讲一个,

signal函数 咱们用的比较多的,
完成一个信号的注册,注册一个用户自定义的函数 言外之意,这个信号是要发生的,就会去调用信号处理函数,
是不是和

这个图这个意思啊,这个代码执行到这个位置之后产生的信号,是不是就会执行信号处理函数啊?

这个函数是用户自己写的,用户自己写的函数,还得调用signal函数完成注册,不然的话,人家怎么知道执行哪个函数啊?

 typedef void (*sighandler t)(int);
sighandler_t signal(int signum,sighandler_t handler);


signum信号的编号,每一个信号是不是都有一个编号啊?数字就是编号,但是你使用的时候用哪个啊?用宏 不要用数字,sighandler_t  第二个参数是信号处理函数的函数名 函数名代表函数的首地址,就是指针嘛 函数指针,那么这个函数指针,
指向这个函数呢,有一个参数 int类型的,那么你说这个int类型是个什么东西?
信号的编号,

前面给大家讲pipe 讲管道的时候是不是给你说
如果说读端全部关闭了,然后你再写管道的话是不是会发生一个,会产生信号啊?

创建了一个管道pipe 我关闭了写端,

 

后面是读吧?我把读端给关闭 让我写,
0是读端,我把读端关闭了
调用signal注册一个信号处理函数,signal在哪调用都行,在关闭读端前面调用也行,

第一个是信号编号,第二个是信号处理函数,我主要是想让大家看一下,到底我把读端关闭以后,然后我再写入管道是不是会产生一个信号,
接下来是不是你要写这个sighandler函数了?
一个void 类型的

 他有一个参数 signo,那么咱们打印一下 这个signo到底是不是这个sigpipe信号,

 sigpipe 是13 打印这个signo是不是13就知道了吧?
产生了13信号,这个信号是谁产生的?内核产生的信号发给当前进程 我把这个代码原理再说一下,
代码很简单,调用pipe创建一个管道,创建管道,是不是在内核就有一个缓冲区了?读写两端来标识这个缓冲区,我们调用signal函数,完成SIGPIPE信号的注册,第一个参数是信号的编号,的个参数是信号的处理函数, 也就是说这个信号发生之后 去处理哪个函数的意思 去执行哪个函数,举个例子,假如说我这个函数执行到 write这个位置了,他一写 你如果写完了 这个时候是不是信号产生了?
因为他是不是正在往一个没有读端的管道写数据啊?内核检测到这个错误了,立刻就产生了一个信号,那么这个信号呢 产生信号是SIGPIPE信号 而且 我们这个当前进程是不是也注册了这么一个信号啊?内核就知道这个信号产生之后,去执行这个sighandler函数,sighandler函数是内核执行的还是你用户进程执行的?是内核执行的,其实这个回调函数,你想一下 回调函数一般是让你自己写这个函数,让别人去执行,

回调函数,实现是用户进行实现的,但是执行,不是用户进行执行的,对于这个sighandler函数,是内核执行的这个函数,那么内核是怎么知道执行这个函数了?因为你已经告诉他了28行,那么这个其实这个注册呢 如果严格的说是这样说
给内核注册一个信号处理函数,是给内核注册的,那么内核检测到这个信号产生了是不是立刻就会执行这个函数了? 它这个函数打印出来这个signo 13
这个是不是验证了咱们前面的说法吧?

给一个没有读端的管道写数据,会产生signo信号,是不是这样的?

信号的特点是,必须在某种特定条件下才会产生,

信号的产生,信号比如说 按键盘,命令 函数 软中断 硬件中断 是不是都会产生信号啊?

信号的状态 信号有3种状态,必达产生状态 未结产生状态 产生状态

信号的四要素
每个信号多有编号,
信号的名字,那个宏就是名字,我们用名字,不要用编号

信号的默认处理动作
大部分的信号的默认处理动作是终止 

因为只有这个进程终止之后才会对系统产生的  (  )最小,
信号是如何产生的,是不是后面两个comment啊

action是不是默认冲突  也就是说这个comment是描述的这个信号是如何产生的,是不是前面咱们前面讲的这个信号扩展的时候说过了,信号是在某种特定条件才会产生吧,这个信号虽然说有这么多个,但是对于我们来说 有些信号需要我们了解一下,但是 这些信号 几个常用到的信号 你大致了解了解,因为这些东西用的相对较多,你得知道,比如说SIGINT 2号编号吧?是不是ctrl+c产生的?

SIGQUIT ctrl+z+/ 使进程退出


SIGKtLL和 SIGSTOP.是不能被捕获 阻塞忽略的,
SIGSEGV 这个信号呢,是非法访问内存会产生的信号
端溢出,内存溢出等等都会产生信号

SIGUSR1、SIGUSR2 这两个信号,是系统留给咱们自己来使用的,是用户信号,这两个信号 我们自己可以来用,其他信号你不要乱发,因为它都是在某种特定条件下产生的,但是你不能乱用,但是这两个信号 SIGUSR1、SIGUSR2  你自己随便用

SIGALRM后面会讲,会产生的信号,
SIGTERM使进程终止,
SIGTERM和SIGKILL有什么区别?都是使进程终止,SIGKILL这个信号不能被捕获,
SIGTERM能够被捕获但是,这个信号的默认处理动作是终止,那么如果说你捕获之后,你可以让他不终止,

SIGCHLD重要的一个信号,这个信号是子进程退出之后,内核会给它的父进程发出一个信号,那么他的父进程收到之后,回收子进程
这就是父进程回收子进程的一个时机


SIGSTOP使进程暂停,
SIGCONT使进程继续执行,

 

信号默认处理动作,信号默认处理是5种,

信号注册,注册由,信号产生之后,会执行信号的注册函数,那个是一个回调函数,那个函数实现是用户实现的,调用是内核调用的,
后面讲信号的处理过程的时候,再说

 

 

kill函数,他对应的系统命令是kill命令,
给指定进程发送指定信号,那么指定进程是怎么标识?是不是进程的pid啊?发送指定信号,那么这个信号怎么标识啊?是不是信号编号啊?
所以说你使用这个函数的时候,肯定是要有两个参数,第一个产生是编号,第二个参数是进程

kill命令在使用的时候是不是也是这样kill -9
9是信号编号,后面来个pid pid就是指的是进程的pid 这就标识了,给哪个进程发送哪个信号

函数原型,int kill(pid_t pid, int sig)
pid_t这个变量的类型见没见过?是不是进程pid啊?进程pid就是这个类型, 第二个是signo 信号的编号

成功返回0  失败返回-1,

pid > 0:发送信号给指定的进程。

pid =0: 发送信号给与调用 kill 函数进程属于同一进程组的所有进程。

举例:父进程循环创建3个子进程,这三个子进程和父进程是不是同一组?如果说你要想把这四个进程一次性全部给杀死, 函数的pid 参数写0,就可以了,那么这个0是什么意思呢?你要给这个当前进程,他自己,调用这个kill函数进程 是不是叫单独进程啊?给他的一个组发,发哪个信号都可以  发个-9就可以了

这样的话一删就可以把这4个进程全部给他杀死,是不是相当于杀了一个组啊?

pid < -1:取|pid|发给对应进程组。
如果pid< -1表示 取pid的绝对值发送给对应进程组,比如说你这个进程组,他的pid 他的组id是100,那么你这用的时候用多少?写-100就可以了
那么写-100表示给这个组发呢,这个东西杀伤面都比较大,一般不用


pid = -1=发送给进程有权限发送的系统中所有进程。
这个杀伤面更大,
如果你在root用户里面调用这么一个函数,

是不是其他的所有的用户全部都受影响啊?因为root用户有最高权限,root用户可以给普通用户发送告急信号,反过来,普通用户能不能给其他用户 或者root用户发送啊? 没有权限,

这四个里面虽然说了4个,但是绝大多数情况下,99.9%的情况你只用第一个,相对安全,假如说 后面的这些参数你都不知道,你只知道>0的这么一个参数,那么你能不能把这四个进程全部杀死?来个循环就可以了

每个进程都属于一个进程组,进程组是一个或多个进程集合,他们相互关联,共同完成一个实体任务,每个进程组都有一个进程组长,默认进程组 ID 与进程组长 ID 相同。

比如说多进程服务,一般情况下都是共同协作 完成一个特定任务,每一个进程组都有一个组长,默认进程组 ID 与进程组长 ID 相同

一个父进程创建三个子进程,很显然肯定是父进程是组长

写一个简单的程序
kill函数测试
kill 也有头文件

kill自杀,

 打印这句话,看看能不能打印出来,如果说没打印出来,说明这个进程已经死掉了,

父进程能不能杀死子进程啊?
可以
子进程能不能杀死父进程?


把进程组的概念也说一下

 

 严格的说不是父进程杀死子进程,而是说父进程给子进程发送信号,kill这个函数其实不是杀死的意思,在这是给谁谁谁发送信号的意思 或者子进程杀死父进程

这个是循环创建三个子进程,

我先把这个进程组的概念先给你提一句

为什么shellp(10) 让他不退出,便于我们观察

 

 

ps -ajx

第一个是父进程,pid 这个是 
pid 是哪个进程的
pgid是组id
sid是会话id

2016是这个kill1进程的父进程 他的父进程应该是-bash啊?

2369是他自己的pid
组的话也是2369他自己

他的会话id 2106 是不是bash啊?

2369 接下来这三个是不是都是子进程啊?这三个子进程很显然 父进程都是2306 子进程自己的pid 各自都不一样  组都是一样的

2369 和pid 2369是一样的 也就是说 这三个子进程 和这一个父进程他们的组id就是父进程的pid
会话id 也是同一会话,2106 是不是bash啊?
我们这四个进程 当然是包括一个父进程 3个子进程 在一个组当中,也在一个会话当中,实际上一个会话会可以包含一个或多个组

在这我们只是看到一个组,通过这个例子我们就看出来了,就是说父进程循环创建3个子进程,这三个子进程和一个父进程 他们在一个组,而且组id 就是父进程的pid

先让子进程 杀死父进程 如果说你在36写 会发生什么情况,如果说我在这kill 严格的说不是杀死父进程,是给父进程发送信号,这个一杀,父进程马上死掉,你说这个 咱们说拥有4个进程?如果说父进程死了,你说这个组还在吗?只要这个组有一个进程存在,那么这个组就不会消失,
 

我这样写会有什么情况发生呢?我这个父进程 刚刚把这一个子进程创建出来以后,后面这两个是不是就没有了,44-54这个咱不管,只要能杀死一个就看了,咱们测试的目的是让子进程杀死父进程啊?已杀死,咱们看一下这个是不是已经创建完了,看来是父进程,他是把时间分析完成之后呢,这个子进程才获得的cpu
子进程是可以杀死父进程的,

反过来,父进程杀死子进程

 放到58 但是你这样你不知道pid 了,所以你只能放到上面,你创建的时候 是不是你知道啊?
发一个if(i=0)就杀第一个,这个给大家验证个什么?就是父子进程间都可以相互杀的
父进程杀死第一个子进程

怎么一个都没杀死啊?他可能跟本就没跑起来就死掉了,还没来得及执行立刻就死掉了,这个大家知道是什么意思就可以了,父子进程之间都可以相互杀死,

进程a 进程b 两个进程之间不是父子关系,这个a知道进程b的pid 他能给他发吗?前提是他们俩是必须处在一个用户下,如果处于两个用户下可以吗?没有权限,
a用户能给b用户发信号吗?发不了吧?
a用户下的进程 不能给b用户下的进程发信号,
 

pid = 0:
发送信号给与调用 kill 函数进程属于同一进程组的所有进程
试一个

 先注掉,当i=0的时候,sleep(1)之后再杀,杀死同一组的所有进程,三个子进程 和一个父进程全部死掉,
pid=-1 的话权限太大了,他这一杀,搞不好连这个bash窗口也给你杀死了,

 

 ps -ef 是不是全杀死了,你看一下有没有子进程活着就知道了,那三个子进程是不是奖会sleep(10)啊?全部死掉了,

试试-1,看看bash这个玩意儿会不会死掉,死掉了,这个是非常危险的,你看有很多人连你的 服务器,你也不知道谁连的 怎么办?直接开一个这玩意儿全部干掉了,或者你这么干,比如说你看到这个bash,比如说你自己就开了一个,是不是只能有一个bash,然后实际上你看到好几个,你那个kill,找到这你只能有一个,找到这个之后,你把他杀死就可以了 他就连不上了

 

 raise函数是给当前进程发送指定信号,自己给自己发,他只能给自己发
你看他的参数就可以了,int raise(int sig) 他没有指定的pid
其实是相当于这个
raise(signo) == kill(getpid(), signo);

谁调用的这个函数,那么当前进程就指的谁,那么其实就是指的他自己啊?

死掉了,14这句话是不是没打出来?
另外一个是abort
给自己发送异常终止信号 6)SIGABRT 不会调用失败,并产生core文件,只能成功,
相当于 kill(getpid(), signo);这个函数会产生6)SIGABRT是6号信号,这个函数会产生6号信号,看一下这个函数怎么用,

 

 

 没有产生,abort 会产生core文件,为什么没有产生core文件呢?因为core文件大小为0,
ulimit-a 看core文件
可以改,ulimit -c unlimited无限制大小,有core文件了,

gdb core 给大家调一下,

这个是不是得加-j啊?这个不给你试了,这个core文件一般产生比较大,

这样的函数abort用处不是太多 简单了解一下

时钟定时器, 有点类似于手机上的时钟,那个是不是设置闹钟的?这个也是,

unsigned int alarm(unsigned int seconds);

unsigned 返回一个无符号整型值,

seconds很显然这是一个描述,这个描述一定是一个大于0 数,设置定时器或闹钟,在指定

seconds 后,内核会给当前进程发送 14 SIGALRM信号

进程收到该信号,默认动作终止。每个进程都有且只有唯一的一个 定时器

是不是终止呢?

默认处理动作是term 终止吧?然后触发Timer signal from
alarm(2)是不是这个函数产生的啊?2是什么意思啊?这个2 是你man的时候 你man 2 alarm

这写3呢?看手册的时候,(2)(3) 要知道什么意思

需要你知道的这一点,每一个进程 有且只有一个唯一的一个 定时器

如果说我弄两个呢?弄两个 最后一个有效,前面的覆盖了,

函数返回值 返回0,或者是剩余的秒数, 如果你是第一次调用,返回0,成功返回0,失败返回一个非0的值,第二次再调用,返回上一次你刚刚定义的那个秒数,

man 2 alarm 没有返回负值的情况,要么返回0,要么返回剩余的秒数

看这个函数应该是不会调用失败,如果一开始的话,你是不是写了一个alarm(5)啊?是不是相当于5秒钟之后会产生一个信号啊?

 

是一个alarm信号, 5秒钟之后会产生一个信号,接下来你sleep(2)休息2秒钟,再调用alarm(5) 返回的是上一个时钟剩余的秒数,上一个时钟是不是已经过了2秒了? 返回3,接下来,是不是再过5秒钟才会产生信号啊?是不是这个最后一个alarm5 把上一个alarm5给覆盖了?那么一个进程只有一个闹钟,只有一个时钟,那么第二个alarm会把第一个alarm覆盖,

还有一个是alarm0如果说你这个时候你不需要了,你写一个alarm0就可以了,alarm0表示取消闹钟的意思,alarm这个时间,括号里面只有秒数啊,那么这个秒数怎么计算的呢?他指的是自然定是法,那么什么叫自然定时法呢?

进程在执行的时候,他是不是有挂起啊,就绪啊,阻塞啊,是不是有很多这样的状态?这个时间跟状态无关,只要你这个alarm只要是写上去了,这个时间开始计算了,到5秒钟的时候呢,他就开始发送信号,你说你这个进程比如说从第一秒启动,比如说在第二秒,第三秒执行完了,你说你的进程是真正的执行了两秒钟吗?不是3-1等于2吧?这个两秒钟期间你这个进程是不是要切换了好几次,因为你这个进程肯定就绪态,进行态,运行态,挂起 等等这些状态,那么这个alarm他不看这个状态的,他看的是自然定时法,这个时间只要过去了就过去了,因为那些状态,你也没有办法计算吧?你知道你的这个程序用了几个cpu时间片吗?
你知道就绪态等待多长时间吗? 挂起态等待多少时间吗?是不是不知道啊?
所以说他的时间是自然定时法,
 

 写个例子测试一下,

我一开始想要设置一个闹钟,接下来打印13这句话,sleep(2)以后,你再alarm(1)然后看他的返回值时几
然后我要让你知道这个alarm发送的是不是一个alarm信号 SIGALRM,

怎么验证呀?是不是咱们得注册信号处理函数了?信号产生之后呢,咱们打开的是不是是一个alarm信号是不是就知道了?
得注册一下啊?注册是不是在前面注册啊?
在这我有一个问题想问一下大家,看他打印的值是不是SIGALRM信号就知道了,是不是6啊?

 就这个信号这一块,有点类似于qt里面的信号槽,根据信号是不是有点类似啊?朝着那个方向理解也可以,那么再问一下大家,现在是不是我注册了一个信号处理函数了, 当这个信号产生之后,那么这个函数就会执行,内核就会去调用,你说我注册了这个函数以后,这个函数刚开始注册的时候会执行吗?这个是一个误区,有的人认为我这写了这一个函数之后16,注册完之后,这个函数就会被调用9-12
这是错误的,不会执行,只有当信号产生之后,9-12这个函数才会被调用,在这是不是写了一个alarm(5)是不是设置一个时钟啊?这个函数调用完之后19,你这个9-12会执行吗?也不会,也是注册一个,是不是给进程设置一个闹钟啊? 但是5秒钟之后,如果说没有22-24的话,这个程序就会发送收到一个信号,SIGALRM 你这样写,运行之后进程结束了,应该让程序再休息一会10秒钟26
如果你不加,这个函数程序是不是瞬间执行完了
瞬间执行完了,时钟还有用吗?进程结束 所有东西全部销毁啊?

一个观察返回值,按理说这应该是几?第一个是0,第二个是3吧?

第二个产生信号你看他是不是6号信号就知道了

 执行alarm0到底能不能把alarm取消呢?

 你说我打印这个28应该是几啊?应该是2,你注册的是2,因为这个时钟已经取消了,接下来要sleep(10)

练习题 2: 编写程序,I测试你的电脑 1秒种能数多个数字

用alarm(1)然后你在你循环里面打印多少个数,1秒钟过后会产生SIGALRM信号,这个信号产生之后默认会终止进程,printf 遇到\n才会打印出来,或者是缓存区满了,如果你这样数的话,是不是死不了了?还用写处理函数吗?alarm(1) 1秒钟过后会发出一个SIGALRM信号,这个信号默认处理动作是不是终止啊?终止了是不是进程退出了?这样的话是不是就可以1秒钟我们打印多少数了?

肯定一开始不会产生sp,只有说,他进入这个循环里面,打印1秒钟过后 产生信号了,这个就终止了,

 real 指的是总共的时间 执行了1.2秒 

user 用户区的时间

sys 内核区的时间

他会涉及到用户区到内核区的切换,那么我这个printf 会不会涉及到用户的切换啊?

printf内部会调用write write 函数会调用sys_write 这个涉及到用户区到内核区的切换

实际执行时间=系统时间 + 用户时间 损耗时间

 

我怎么让我这个值打印的多一点呢? 我把这个回车去掉行不行?去掉应该打印的多一点吧?你这样打的话是不是相当于每一个数都打啊?每一个数是不是都会泄露一个用户区到内核区的切换啊?你把这个\n去掉呢?这样切换的次数是不是大致减少了?

 我重定向一个文件当中去,你说这个 这个是不是相当于写文件操作了?那么写文件操作是不是相当于带缓冲呐?一个带缓冲 一个不带缓冲 哪个效率高?带缓冲的效率高 如果说你在写代码的时候,如果说你在写文件操作,fopen fclose fread这样的函数 ,你不要用openread write一个带缓冲,一个不带缓冲

 从这个时间来看的话,实际执行时间接近1秒,而且用户区+ 内核区一共0.948 很显然这个损耗是非常小,

文件重定向效率高很多,为什么效率高?

为什么效率高?为什么24这么执行效率比较低?原因是:

所以涉及到用户区到内核区的切换次数大大减少

一个是文件重定向,一个是直接打印,那么

他们的效率差别很大,差在哪 主要就是由于从用户态到内核区的切换的时候呢,损耗的时间,那么这个时间是不是很浪费时间啊?

 

setitimer比alarm函数的优势在哪呢?alarm函数是不是只能调用一次alarm函数是不是只能产生一个alarm信号啊?

一个alarm函数只能产生一个,alarm函数那个参数指的是时间吧?到那个时间点他只能发送一个信号,但是我们有这样的一个需求,我们想周期性的触发,不只是让他产生一次,这个时候你用alarm不太好使了,你是不是可以在whele循环里面不停的让他触发也行啊?相对来说比较麻烦一些,相对更好的实现方法叫setitimer

这个函数返回值 成功返回0,失败返回-1,

参数意思 可以周期性的产生信号,

which指定定时的方式,到了那个时间点以后呢,他产生的信号是SIGALRM信号,这个和alarm一样,alarm是不是用的也是自然定时法,那么这个自然时间里面是不是这个进程的状态不管什么他都计时啊?不管是sleep也好,就绪,运行 时间都算,

虚拟空间计时(用户空间):ITIMER VIRTUAL> 26)他产生的信号是SIGVTALRM,这个一般情况下我们用的不多,这个计算 他只是计算到用户区的时间,我们这个进程执行的话是不是有时候在用户态 有的时候在内核态?

后面

运行时计时(用户+内核):行系统调用的时间 这个包含了损耗的时间了,包含了用户的休眠 挂起啊等等,这个时间你能计算的准吗?

所以说这三个里面,虽然说他介绍了3个,一般情况下用自然定时,所以说你把这个掌握了就可以了

后面 

第二个参数是const structitimerval *new_value  是一个结构体,你看到参数的时候 如果你看到前面有一个const表明他一定是一个输入参数,那么这个结构体,稍微麻烦一些

structitimerval 这个结构体有两个成员,而且每一个成员是不是也是结构体啊?

 这个是不是相当于结构体嵌套啊?这个类似于这个,假如说这个大结构体是a

a内有个结构体叫b  b 里面有个成员叫c 那么你怎么引用这个c啊? 这个c怎么用啊?

是不是a.b.c 如果a是一个指针呢?是不是a->b.c啊

 struct timeval 只有这两个成员一个是,long tv_sec;秒
long  tv_usec;毫秒

这个意思是精准度很高,你这个alarm只能精确到秒,

我举个例子,假如说我想设置3.5秒 这个时间怎么设啊?tv_sec =3

tv_usec=0.5 当然你写的话,不能写0.5秒  1秒1000微秒 

tv_usec=500就可以了

这个时间是不是两个加在一起啊?

old_value 存放旧的 timeout 值,一般指定为 NULL 如果说你在一个函数声明里面看old 什么的,他是一个传出参数,他存放的是一个先前的一个设置,原来的一个设置,如果说你第一次用这个的话你会有原来的设置吗?第二次再次调用是不是有了?

一般我们这里设置为null就可以了

这样的话,需要传参的话,这个which也需要传参啊?当然这个which 传ITIMER REAL>

对这个*new_value的话你怎么赋值啊?你比如说你第一变量用tm 是tm.it interval.tv_sec;赋值

然后再. tv_usec;再赋值

这个struct itimerval 结构体有两个成员,

这两个一共有两个结构体,一个是it_interval;闹钟的触发周期 一个是 it _value;闹钟的触发时间

这两个是什么关系呢?,

举个例子,我们想让我们的进程呢,什么时候产生闹钟 时钟呢?3秒钟过后 每隔1秒钟产生,

这个3应该在it_value赋值,具体赋值在

long tvIsec;=3

long tv_usec;=0

每隔1秒钟,接下来it interval 每隔1秒钟 意思是周期性的意思,

it_ interval.tvIsec=1 .tv_usec=0

大家要对这个结构体的用法要熟悉起来,因为咱们后面再讲这个socket atmi原型的时候像这样的用法还有很多,

像这种类似的用法,结构体套结构体

为什么用结构体套结构体?一个是结构的性质,再一个是可以能够表示更多的信息,那么你使用结构体的话是不是就可以表示更多的信息啊?

如果说你不使用结构体,就让你传什么啊?传整型值,是不是也可以? 你是不是得传4个啊?

相对来说麻烦一些

周期性

功能设置时钟 能够周期性的触发时钟 

每个参数看手册 看文档, 

ITIMER REAL 是不是有没有过期的意思,发送信号递送传达信号

三秒钟过后每隔一秒触发一次,结构体是两个成员

定义一个 itimerval 周期赋值的意思,有两个成员 赋值两次,

tV_usec:你不写的话他是不是有可能是随机值啊?随机值有可能会很大,有可能会很小啊?

我们这个变量是不是在栈上?这个你不赋值的话,他就是一个随机值,

这个是什么时候触发,第一次触发的时间吧?

3秒钟过后,每隔1秒钟 就会产生 一个SIGVTALRM信号,

接下来你才去调用这个setitimer 前面的那些都是赋值啊,这个是不是相当于注册一个时钟啊?或者安装一个时钟,只不过这个时钟是一个周期性的,alarm是不是就触发一次啊?

第一个要用自然定时法,第二个是指针,需要把这个tm地址变量传给他啊?第三个不关心传null

你如果仅仅这样写的话,你能够检测到这个他到底有没有产生SIGVTALRM信号吗?因为这个SIGVTALRM信号默认是终止,

那你这样写,肯定不行是不是还没有触发就退出了?所以第一个是我们是保证不让他退出吧?

所以sleep(1)这样我的这个进程是不是不退出啊?

我们看一下这个setitimer函数,他到底发送的是不是SIGVTALRM信号吧?我们看一下这个信号的注册啊?

 这个函数signal调用完以后,不是说立刻就产生信号,这个只是一个注册,只有说这个信号产生之后,他注册的信号处理函数才会被执行,打印的是14编号

 那么也就是说咱们这个程序 一开始先过3秒,是不是没什么动作啊?然后过3秒之后,每隔1秒钟13这句话,会打印出来,这个是一个周期性触发,

终止进程,这个 代码里面我们先注册一个信号处理函数,我们注册的是SIGALRM 信号,  信号处理函数是sighandIer 接下来我们设置一个周期性的时钟,这个结构体怎么赋值,你一定要学会,这个结构体是不是结构体里面套结构体啊?那么你用的时候要这么用tm.it_interval.tv_usec =0;

相当于a.b.c

大的结构体是在最外面, 小的结构体在里面,第一次触发的时间在3秒钟,过3秒钟开始每隔1秒钟

实际上 第一次时钟产生是第几秒啊?应该是第4秒了,到3秒了,每隔一秒钟,第四秒产生一个,然后第5秒钟1个,第6秒钟1个,每隔一秒钟产生一个,比如说第一次产生是1秒钟,第二秒不产生第三秒又来一个啊?每隔一秒钟

已经捕获了,捕获的意思是按照你的方式来啊?

14号信号是term 默认是中 当然你要捕获,他还会执行默认动作吗?你捕获他就不再执行默认动作了 我按ctrl+c  3秒钟过后,每隔一秒钟产生一个信号,这个打印这句话,是不是每隔1秒钟打印一次这句话?回到while(1)sleep(1)那里去了?这个时候我按ctrl+c 是不是终止啊?ctrl+c 发送哪个信号啊?SIGINT 2号信号 2号信号默认处理动作是什么?终止进程,因为我们没有对SIGINT进行捕获,如果你捕获了,你可以让他不终止,也就是说你这个信号处理完以后 12  13  14,你这个信号是不是已经执行完了?

执行完以后他应该是回到了while 35 -39循环里面吧?1秒钟过后又立刻产生这么一个信号了11-14? 产生是不是再次调用啊?再完成以后再回到36-38这里面来

如果你没有这个sleep 1 你这个进程是不是马上就退出了?

你退出之后还有什么时钟啊?

信号集有两个重要的概念,一个叫阻塞信号集 一个叫未决信号集 信号相关的内容,都保存到内核当中的pcb当中啊?那么大的结构体,其实你查看这个tas strus的时候应该能看到sigset_t 这个信号的变量,那个就是信号集,信号相关的东西,就在内核当中,什么是未决信号集 什么是阻塞信号集  未决信号集里面保存的都是没有被处理的信号,咱们前面在讲基本概念的时候是不是给你说了?信号的几个状态,产生信号是怎么产生的,未决的意思是不是就是这个信号没有被处理的?

没有被处理的信号叫未决信号集,还有一个叫地达 地达的意思是这个信号已经被处理了,这个未决信号集的意思应该就是这个集合里面保存的都是没有被处理的信号吧?阻塞信号集的意思是我们的这个进程 有时候需要让一些信号先不处理,这个时候你可以把这个信号给他阻塞,阻塞 很多信号,比如说有些信号需要阻塞,那么可以把这些信号保留在阻塞信号集里面去,那这个就是阻塞信号集的意思,是不是当前进程需要被当前进程阻塞的信号啊?的一个集合就叫阻塞信号集,这两个信号到底是什么关系?在这我重点给你说明这一个

这两个信号是什么关系?

我以一个信号为例,2号信号 ctrl+c 以这个为例来解释一下 这两个信号到底是什么关系,我们这一个进程启动起来以后,一个程序一执行,是不是相当于进程启动起来了?就成了一个进程,那么我们这个进程有的时候能够收到一些信号,比如说2号信号,那么一个,我们进程收到这个2号信号以后呢,那么他首先就留在了这个未决信号集当中,

比如说2号信号,一开始的时候呢,他先留在未决信号集当中,此时他的值为1,在我们信号集当中,这个信号集他的变量叫sigset_t 你这种就是一个数据类型,你可以把这想象成int  anset  sort  这就是一个数据类型,只不过这个类型是不是定义的用tyf 定义的?

 这个具体是多少呢?我在这里给你列出来了

 unsigned long int 是不是这个就是他的数据类型啊?你不要把他想的太复杂,你就认为他是一个信号集 就是一个变量类型就行了,不要想得太复杂 你就把他想象成 int 或者longint 都行,

sigset t 这个类型刚刚看了,他是一个 unsigned long 

这个类型,我们用的时候怎么用呢?他是用了这个里面的位操作,只要涉及到位 操作,那么他只有两种值,0或1,0表示没有 1 表示有 在我们这呢 1 2  3 一直往后排,这里面1保留的是1号信号, 2 保留的是2号信号, 3保留的是3号信号,那么没有编号为0的信号吧?这个信号一定是从0开始的,没有编号为0的信号,

刚刚我们说了 2号信号现在,我们这个进程是不是收到了 2号信号啊?这个时候呢?先保留在未决信号集当中,此时值为1,是不是表示这个信号还没有被处理呢?

当这个信号需要被处理之前,他先到阻塞信号集里面去查询了一下,这个信号有没有被阻塞?他要看他对应的位置上 那么这个是2号编号吧?在这呢,他也查看2号编号,假如说这个编号为0,表示这个信号没有被阻塞,这个编号为0,表示阻塞信号集当中没有这个2号编号 的信号吧?

也就是说这个信号没有被阻塞, 那么这个信号他就能够被处理, 也就是说不被阻塞,当这个信号

2号信号他被处理完以后,这个结果,这块2号这个的标识位由1变成0

也就是说你这个信号到底需不需要被处理,有一个先决条件 要看他是不是被阻塞,如果被阻塞了,那么 他就不处理,如果没阻塞,是不是这个信号,他处理完以后 那么这个2号编号对应的标识位上由1变成0标识该信号已经地达了,地达是不是被处理的意思,反过来 假如说在查看阻塞信号集的时候呢 这个位不是0,而是1 1表示这个信号被阻塞,那么这个未决信号集里面2号编号,对应的这个位上的1 仍然保留,表示这个信号留在了未决信号集当中,也就是说实际上这个1是不是没变呐?仍然是1,这就表示被阻塞了,

 

 

 但是注意一点,假如说后续操作 我们把这个阻塞信号集当中这个2号编号对应的位上这个阻塞信号集当中这个2号信号给他解除阻塞 是不是有阻塞 就有解除阻塞啊?

解除阻塞 也就是说这的值 这个1变成0了,他这个以后呢,是不是表示这个信号显示解除阻塞了?

 那就意味着 未决信号集里面的这个信号,仍然需要被处理 他处理完以后呢 这个信号2号编号对应的标识位他仍然由1 变成了0 表示这个信号已经被处理了

通过刚刚描述的这段话来说,这个未决信号集里面, 这个信号是不是说只是暂时不会处理啊?那么他到底能不能处理 就是该不该处理 取决于阻塞信号集当中这个对应的标识位上,他是不是被阻塞了,如果没被阻塞那就意味着需要处理,如果阻塞了那就需要什么?这个未决信号集这个位是不是保持为1啊? 后续如果说呢 这个进程当中呢,这个阻塞信号集当中这个2号编号,这个位上由1变成0了,那么就意味着什么?这个信号是不是解除阻塞了?未决信号集里面对应的这个位上是不是 因为他一开始是不是未决啊?是不是后续还需要被处理啊?处理完以后是不是由1变成0了?

这里面大家要记住一点的是,这个信号,未决信号集里面的信号,第一,他需不需要被处理,取决于阻塞信号集当中的标识位上是不是为1,那么也就是说你这个信号有没有被阻塞吧?如果没有被阻塞 需要处理,如果阻塞了暂时不会处理,大家注意了,我说的是暂时吧?当然以后信号集解除阻塞之后这个信号还是需要被处理,既然说到处理 处理的方式有几种?一个信号处理方式有几种?忽略 捕获,捕获我们是不是可以调用调用signal处理信号处理函数啊?

然后按照我们自己的方式来处理吧?

第三种是不是默认动作啊,大部分默认处理动作是终止,

这个里面未决信号集画的这个框框,还有这个框框,其实就是sigset t类型的变量,这个变量里面有很多位啊?每一位上只有两种值,0表示没有,1表示有,就这么简单,

以2号信号为例,写一下这个过程,当进程收到SIGINT信号后,首先被保留在未决信号集中,这当这个信号被处理之前,先检查阻塞信号集对应的编号的位,

这个未决信号集对应的编号是2 因为我们这个cd的信号是2号信号,所以说他是2号信号位上,此时此刻他是不是为1啊?

为1是不是表示他已经在未决信号集里面来了?当这个信号被处理之前呢?先检测阻塞信号集中对应的编号的位上 的标识位是否为1:为1表示该信号被当前进程阻塞了,此时该信号暂时不被处理,对应的标识位 仍然标记为1,依旧是表示留在这个信号集当中吧?

为0表示该信号没有被当前进程  阻塞和不阻塞都是指的是当前进程的意思,a进程信号和b进程信号没有任何的关系,表示没有被当前进程阻塞,阻塞还有另外一个称呼叫屏蔽 这个阻塞信号集 也叫屏蔽信号集 说的是一个意思,则未决信号其中的信号,需要被处理(忽略 执行默认动作 ,执行用户自定义函数 或叫捕获被当前进程捕获  ) 当信号被处理完成后,未决信号集中的这个标识位从1---->0

这个里面主要是2号编号 对应的标识位上 值的由1变成0,表示该信号已经抵达了(已经被处理了).

内核当中有两个信号集 一个叫未决信号集 一个叫阻塞信号集 那么他俩之间有什么关系呢?咱们以2号信号为例,来说明一下,当进程收到一个SIGINT信号之后,那么这个信号首先会保留到未决信号集,对应的编号位置上,这个SIGINT是几号编号啊?2号编号,所以他保留到了2号编号上,此时的标识位为1,当这个信号被处理之前,先到阻塞信号集去查看一下,对应编号的位置上面的值,大家注意要看清楚,你这是2 你不能查看3, 唯一表示该信号被当前进程阻塞了,那就意味着

我们未决信号当中信号暂时不需要被处理,此时标识位仍然为1,假使说检查到这个阻塞信号集当中这个值 对应编号这个值 是0,表示这个信号没有被当前进程阻塞,那就意味着 这个未决集当中信号是需要被处理的,那处理完以后 这个信号对应的标识位上由1变成0,表示这个信号已经抵达了,那就意味着 已经被处理完了,整体过程就这样子,仔细观察一下我们这几个未决信号集 和阻塞信号集这两个信号集 这两个信号集他的数据类型都是sigset t类型,你看我这1号编号 2号编号 3号编号也好,这所有的编号上,是不是标识着每个信号 的意思,那么这个每个编号上面对应的值是不是只有两种,1表示有 0表示没有,你说我这个信号假如说  我举个例子 假如说我这个未决信号集2号编号 此时为1吧?然后呢他又被阻塞了 是不是这个信号暂时不会处理 那假如说我这个时候 进程又收到这个信号,还是2号信号,那么你说最后我这个阻塞信号集当中,这个信号被解除阻塞之后,我这个信号会被处理几次? 为什么被处理1次啊?因为他并不知道你这个到底是发生了几次?你看你这个位上是不是只记录了1啊? 1表示有 0表示没有,他只是告诉了你有或没有?但有几个他知道吗?从这个上面是不是你能够分析出来,这个信号他是不支持排队的啊?这个排队 怎么理解?这个2号线 是否被阻塞了,被阻塞期间,我这个2号线发生了多次 有没有可能啊?这很简单吧,你按键盘的快捷键 多按几次是不是就出来了? 

大家注意了,也就是说这个信号不支持排队,你阻塞的期间产生了多次,阻塞期间产生了多次

最后解除阻塞之后 该信号只被处理一次, 从哪标记 他发生了多次了?没有标记出来吧?只能执行一次,这两个阻塞信号集 与未决信号集之间的关系,要求你能够自己口述出来,而且能够说对,我建议你把这些东西理解之后,自己写一写,不要怕麻烦

 

 

这些函数你能够参照将以 或者手册  你能够写相关的函数 例子就可以了 ,这是对大家的要求,

不需要你现在死记硬背了,但是你看到这个函数之后,你应该知道他是什么意思,

这几个函数都有一个特点 都有sigset_t类型啊?

 那么这个sigset_t我在上面给大家从底层的头文件里面也给你找了一下,找到这个sigset_t类型的定义在signal.h里面 现在我就不带着你往这个文件里面找了,我给你简单说一下,总之你要知道这个sigset_t 他就是一个数据类型,你也不要把他想的太复杂了,你不要想他是什么int  long 都行,不要感到很陌生 

实际上他就是这种类型

 unsigned long int 

int 是4个字节, long是不是8个字节啊?8个字节一共有多少位啊? 8*8 64吧? 那么我看一下这个信号一共有多少个信号啊?

正好有64个信号  

一个位是不是表示一个编号啊?你这么来想就可以了,这是这个数据类型,叫sigset t;

同学们如果你在看这个类型的时候,你不知道怎么看,我这个是不是交给你小窍门了?gcc -E test.c -o test.i是不是你预处理之后,是不是可以看到这个变量类型了?预处理之后 包括这些宏什么的,这些宏是不是预处理之后 是不是这些宏都变了,是不是已经换了_SIGSET NWORDS];

你这样来做就可以了,这个可以试一下,下面咱把这几个函数,一个一个来说一下,sigemptyset(sigset_t  *set)

一个一个说一下,

sigemptyset(sigset_t  *set)这个函数 是清空的意思,清空信号集

这个是sigset_t 一个类型变量吧?这两个变量 未决信号集 和阻塞信号集 这两个集合 你能不能直接操作?

切记 不行,当你执行一个sigset_t set; 这么一个操作之后呢 相当于什么?相当于把这里面所有的的位置为1还是0? 0   传值的时候是不是把这个set地址传进去啊?

相当于把这个集合当中的所有的位上全置为0,你这个位上的值是不是只有两种啊?0和1  0是不是表示没有啊? 开始的时候你肯定是不是集合相当于做了个初始化操作啊?相当于memset 

intrsigfillset(sigset_t *set); 这个意思是相当于把这个集合当中所有的位 变成1了,将集合中所有的位设置位1,这个函数执行完以后呢,是不是成这个样子了?

当然你这个sigset t set;你是不是得把这个and符set设置进去啊?这个完了之后就成这个样子了,是不是成了都是0了? 这个函数我们一般很少用, 他太绝对了

👆 

将int sigaddset(sigset t *set, int signum);  将某个信号加入到集合中 信号集中 现在我说这个信号集是不是没有阻塞信号集 未决信号集啊?反正通通是一个集合吗?假如说我举个例子,sigaddset(&set,SIGINT)执行过这个操作之后变成什么样子了?这些是不是相当于我都去掉了?

你在做add之前 你先做什么?是不是先做初始化啊?正常应该是这个样子,先全是0,

 当你做sigaddset 这个是不是取set地址啊?然后SIGINT然后 这个时候 2号编号对应的位上,是不是由0变成1了? 是不是我就认为这个2号编号已经加入到这个集合当中来了?

👆 

int sigdelset(sigset t *set, int signum); 是不是将某个信号从这个集合当中移除啊?或者去掉 去除 

例如:sigdelset(&set,SIGINT);

是不是这个1又变成0了?2号编号又变成0了 表示这个SIGINT这个2号信号已不在这个集合当中了吧?

👆 

int sigismember(const sigset t *set, int signum) 是查看一下 这个信号是不是在这个集合当中的意思,说白了就是一个if判断,

假如说3号信号现在是1   例如

此时这个返回真还是返回假 sigismember  就是说在不在这个里面的意思吧?肯定是只有两种结果吧? 在 返回真  不在返回假,这个三号信号对应的位置是不是为1啊?表示3号信号在这个集合当中,返回值写代码的时候再说

sigprocmask函数 这个函数非常重要,

函数说明: 用来屏蔽信号、解除屏蔽也使用该函数。其本质,读或修改进程控制块中的信号屏蔽字 (阻塞信号集)。

用来阻塞信号 或解除阻塞信号,都是用这个函数, 既然说 你阻塞 和非阻塞都用这个的话,他是不是得有一个参数区分一下啊?看这个函数怎么用?

int sigprocmask(int how, const sigset t *set, sigset t *oldset)

这个函数两个功能 一个是阻塞  使某个信号阻塞, 一个是解除阻塞 都用这个函数,

how 参数取值 这个how表示你想做什么样的操作

 SIG_BLOCK 这个how设置为SIG_BLOCK表示你要将set 这个集合当中这些信号 要加入到阻塞信号集当中去,你调用这个函数 其实是给内核打交道  你调用这个函数 相当于要访问内核的阻塞信号集 

 刚才 我给大家讲的这一堆函数,有没有涉及到内核的数据啊?是不是我都是在这个栈上的,我自己这个上面说的?内核集里面  内核里面的数据 我是不是没有说啊?一直是在我自己定义的这个上面去说的 , 第一个是 how的话 第一个是SIG BLOCK:表示你要把这个set集合当中,凡是为1的

为1是不是要表示你要想把某一信号。是不是加入到阻塞信号集当中去? 表示要阻塞信号,相当于你调用了 mask = mask|set  |是不是 相当于添加的意思啊?

现在我想调用一个函数 叫 sigprocmask我要将某些信号加入到阻塞信号集当中去, 我怎么做到的呢?我再给你填两个 比如说这个6号为1, 4号信号也为1,是不是我sigaddset进去的,这个相当于来说这个集合里面,这个集合里面唯一的位,都有哪几个编号信号啊?是不是这个三号 四号 六号都为1吧?在这6个信号都在这个集合当中吧?

现在我调用了sigprocmask 我第一个参数 我用的是SIG BLOCK 表示我开始设置阻塞了, 第二个

我用了个 &set  第三个 原来的设置我现在不关心 我设置为null  我上午的时候是不是讲过什么什么old的?settime 表示先前的设置,我们这个sigprocmask第三个参数也是先前的设置,现在我们对第三个参数不关心 直接设置为null 

我做了这一步操作以后,在内核当中是怎么表现的呢?现在我这个三四六都在这个信号集当中,当你调用了这个sigprocmask函数以后,他做了一步这样的操作,他其实是将这个集合当中所有位为1的 他只看为1的,全把他填到阻塞信号集对应的位上 也变为1,现在来说这个三四六 是不是都为1啊?调用这个函数引用这为1, 4 5 6 为1  也就是这个3 对应的是这个位置啊?4 对应的是这个位置啊?6 对应的是这个位置啊? 其他的是不是都为0啊?

现在来说我这个阻塞信号集当中是不是有阻塞信号啊?也就是说我阻塞哪个信号啊?是不是  3 4  6 啊?

这做了一个这样的操作,这个就是 sigprocmask所做的事情,就是sigprocmask做的事情,第二个参数 是不是相反啊?他其实就相当于SIG UNBLOCK:相当干mask = mask & ~set 

在演示一下怎么回事,比如说我们这个集合还是3   4  6  为 1, 当然你这个对应位置上已经设置为1了, 其他的都是0,现在这个阻塞信号集上 是不是只有3  4  6 为1啊?那表示当前进程阻塞的是3  4  6吧?当你执行了这样一步操作,叫sigprocmask(SIG UNBLOCK,&set, NULL);当你做了 这么一个操作 相当于 你这个集合相当于这个 3  4  6 为1啊?接下来 他就把阻塞信号集上对应的编号位上的1给你变成0,现在来说,既然说你这个集合当中,我这个自定义集合当中  3 4  6 为1  他就把你这个阻塞信号集当中的对应的编号上 对应的编号是不是也是  3 4  6 由1变成0,这 是不是第二个也成0了?

也就是现在来说 这三个信号是不是已经从左侧信号集当中去除了?既然说他从阻塞信号集当中给去除了,那就意味着如果说未决信号集里面有这个信号 这个信号马上就被处理了,这就是这SIG_UNBLOCK,做的事情,还有一个参数叫SIG_SETMASKG 这个你想也能想的出来吧 这个是不是相当于赋值,这个比如说咱们这个chmd命令相当于等号SIG_SETMASK 这个我们一般慎用 这个你一改不得了,会把原型的设置给破坏掉啊?会覆盖掉,这个其实要结合第三个参数来用,如果你要设置,如果你要用这个c端 setmast 那么你要结合第三个参数,是不是我可以把先前的阻塞信号集先获取啊?获取之后 然后我再进行添加或修改是不是也可以呀?因为这种不要直接覆盖,大家记得fconur那个函数吧?获取获取文件flag标识,先获取  再设置,最后再setflag

sigpending函数

现在我说了半天是不是说到阻塞信号集了?
 那么未决信号集当中的信号 我们能不能直接读取?不可以 包括阻塞信号集  这些是不是你都不能直接获取?

你要想读取  你必须用他提供的接口,在这 系统给我提供的一个接口,叫sigpending(&set)这个只有一个参数,&set 比如说咱们调用了sigpending函数,那么调用这个sigpending函数以后呢,其实就达到了一个这样的效果,内核会将未决信号集里面的,这些凡是为1的信号,或者是他把未决信号集 这个整个集合 给你mmcopy到了 你用户自定义的集合当中来了,sigset t set; 是不是做了一个这样的操作啊?整个是不是未决信号集啊?他整个都拷出来了,为什么可以直接拷?为什么可以直接mmcopy出来啊?因为他俩是相同的变量啊?

是不是相同类型的变量啊?可以直接mmcopy出来,你是不能够直接读取未决信号集当中的数据的,但是你是可以通过结果来访问 那这个时候呢?我们制定sigpending以后呢?整个未决信号集里面的数据我们完全拿出来了吧?这个时候我问一下大家,我怎么判断 某个信号在不在 未决信号集当中?怎么判断?是不是sigismember 你先把这个未决信号集里面 这些数据全拿到本地变量来吧?这个本地变量是不是一个栈上面啊?是在本地的栈空间上的,然后呢你再调用sigismember来判断一下某个信号是不是在集合当中 是不是就可以了?这样的话前前后后这几个函数我给大家说过了,函数介绍过 你记不住,也没有关系  别着急 先不用记他 咱们可以通过一个案例  把这几个函数统一的用一遍 最后你把这几个案例看明白就可以了

练习: 编写程序,设置阻塞信号集并把所有常规信号的未决状态打印至屏幕。

什么叫常规信号呢?其实就是前面的1到31号 这个你可以定  你可以定1-16也行,这个没有关系的 咱们 就用几个就行了,咱们可以把这两个信号  这个信号是不是我们可以用键盘来 发出来啊?

2)SIGINT   3)SIGQUIT 是ctrl+/ 这个是不是也可以啊?

这两个信号我们可以捕获一下,咱们就以这个信号为例 来写一下,这个的 意思是不是我们先设置阻塞信号集啊?

大致过程应该是这样的,我们将这两个信号  2号信号和3 号信号设置为阻塞信号集,然后我们这个进程启动起来以后 首先是不是应该设置阻塞信号集啊?

设置好以后 接下来 ,应该是在一个循环里面循环啊? 不然的话是不是很快就退出了?然后呢 我们是不是按键盘产生信号 ?那么产生信号之后,由于这两个信号被阻塞了,他是不是会留在了未决信号集当中啊?然后接下来咱们再调用sigpending是获取这个未决信号集啊?接下来后续的操作  调用sigismember 来判断一下 这个信号是不是在这个集合当中啊?然后我们再打印出来不就可以了吗?

要想使用信号集 第一步先干什么? 首先定义一个信号集变量

第二个定义之后你该干什么?是不是进行初始化操作啊?初始化信号集

第三步 已经是初始化了,现在这个集合当中所有的位全置为零,将某个信号加入到集合当中去,

 SIGINT SIGQUIT 为什么用这两个信号呢?因为这两个信号我们操作起来方便,加入到set集合中

这两个集合当中,是不是已经加入到了 我们自己定义的这个set当中啊?现在阻塞信号集有没有发生变化?没有,接下来你是不是要将这两个信号加入到阻塞信号集当中啊?

这两个信号是不是已经到这个set当中来了?加入到阻塞信号集中

第一个参数是不是我要阻塞信号集啊?SIG_BLOCK 第一个值是how how有三种值,我们现在是将某些信号集加入到阻塞信号集当中,所以很显然 我们应该用SIG_BLOCK  第二个参数是set 第三个参数不关心 ,你先给他设置为null 后面我给你讲一下 怎么用这个参数,这个是不是已经进来了?

做到这一步之后,其实我们这个进程已经能够将这两个信号给屏蔽掉了,这个时候,如果说我们进程收到了这两个信号,那么你这两个信号跑哪去了?是不是保留到了未决信号集当中啊?因为他以这两个信号已经被阻塞了,所以说这两个信号不需要被处理SIGINT  SIGQLIT) 暂时不需要被处理,接下来咱们进入一个while循环, 然后咱们反复的来观测一下

想一下 现在的话,如果说我们想看一下 未决信号集里面有哪些信号,我们怎么看?是不是先获得未决信号集里面的信号啊?怎么获得啊? 获取 未决信号集里面的信号

是不是仍然是你需要定义一个变量啊?这个变量是不是用来保存未决信号集的?所以在这我们需要定义一个变量,变量怎么定义? 叫sigset_t pend; 在你使用之前要记得初始化,这个是不是我们反复使用啊?所以你要反复的初始化,也就是现在的话这一个,如果说当前进程的话有未决信号的,通过这个函数 的调用  当然这个初始化 通过调用sigpending函数是不是可以把未决信号集,这些数据拿到我们本地变量来啊?

 这个是不是将未决信号集当中的信号,已经获取到本地变量里面来了,接下来,我们判断一下哪个信号在这个集合当中 怎么判断?

是不是调用sigismember在这我们判断的是1-31号信号,是不是来个for循环就可以了,

没有编号从0开始的, 小于32 到31 i是信号编号的意思,是不是编号从1到31啊?当然是不是通过它是不是也是循环因子啊? 这个循环因子 就指的是信号的编号 是不是和那宏是一个意思啊?接下来 我们判断怎么判断?调用sigismember 这个sigismember  tests l检测这个信号是不是这个集合的一员 意思就是在它当中吧?
 sigismember() return 1 如果信号在这个集合中,返回0,if signum is not a member 是不是不在这个结构当中啊?返回-1,发生错误, 在这我只需判断 只要是1 我们认为在集合当中,不是1,我们认为不在,我们判断是否等于1就可以了,第一个参数是一个集合,写pend  i ==1 1 是不是在的意思,在这里先不要加回车如果你要加回车,是不是不好看啊?不好观察, else 是不是不在的意思?不在打印0,

在for循环打印一个回车,

如果你这样打的话是不是循环太快了?  加一个sleep1

 避免太快你不容易观察,看这个代码有没有什么 前面将两个信号加入到阻塞信号集当中,先定义 再初始化,然后再add 然后再调用sigprocmask就可以了吧?这样的话这两个信号是不是就加入到了这个阻塞信号集当中了?然后进入while1循环,我们获取未决信号集是当前sigpending(&pend);进程的未决信号集,36  然后我们从1到31号信号,咱们调用sigismember来判断一下这个信号是不是在这个pend当中吧 pend 是不是就指的是未决信号集吧?当然这个值是我们本地变量,但是我们已经调用这个函数,把未决信号集sigpending(&pend);的值全部拷贝到了本地变量当中来了?

现在的话,我们没有产生这个信号,那么未决信号集当中 当然没有东西了吧?

 接下来是不是我要产生信号了?按ctrl+c  按ctrl+\是不是三号进程也进来了?这个时候你按ctrl+c还能终止吗?是不是终止不了啊? 咱们只能怎么退出啊?

 是不是在外面杀死啊?是不是死掉了?接下来咱们继续把这个程序完善一下,怎么完善呢?一开始是不是阻塞啊?咱们循环10次 让他解除阻塞一次,解除阻塞以后 让他去执行用户自定义的函数, 也就是我们需要捕获他吧?那么捕获调用哪个函数,我们是不是需要注册一个信号处理函数吧?signal(SIGINT, sighandler); 第一个参数是信号的编号, 第二个是信号的处理函数,我这两个信号能不能共用一个信号处理函数啊?当然可以啊,

 因为这个编号是不是我能看得出来啊?这两个编号是不一样的,一个是2一个是3,是2 肯定是调用的实际上处理的是2号编号,是3肯定是调用的实际上处理的是3号编号,

这两个现在我们已经注册完成了,我们给内核注册了两个信号处理函数?当然我们是共用一个 这也没问题 咱们如何让他解除阻塞啊?咱们循环10次让他解除阻塞,接下来还是得调用sigprocmask解除阻塞吧? 怎么让他解除阻塞啊? 

如何循环10次我又解除一次? 只要是0的倍数,他就等于0了吧?如果是10的倍数,让他解除阻塞一次,10的倍数解除阻塞,不是10的倍数 继续保持阻塞

 咱们测的时候你会发现一个现象,当循环10次以后,我们这个信号处理函数是不是都会执行一次啊?前提是你得有未决信号吧?

接下来,我再产生,我按了几次啊?我按ctrl+c是不是按了多次啊?只执行一次? 这个也能证明我们信号是不支持排队的,也就是说这个信号产生期间,如果说是产生了多次,最后只被执行一次

10次之后,解除阻塞, 未决信号集这两个函数是不是需要被执行了?

sigprocmask是不是还有一个参数呢?SIG_SETMASK 这个咱们结合这个sigprocmask第三个参数来用,

如果说你获取原有的信号集的话,我们最后再恢复原状,是不是也相当于解除阻塞了?因为你所有的信号都没有被阻塞啊?

再定义一个变量,sigset_t oldset; 这个你也得初始化,

 恢复怎么恢复呢?你只需要恢复oldset就可以了 因为oldset并没有这两个吧?

 使用oldset恢复原状了?原来因为这两个信号就没有被阻塞 所以呢,你这样设置是可以的,其实一开始的时候,所有的信号,都没有被阻塞,到底阻塞不阻塞是用户进程说了算 是不是这样的?

和原来一样 通过我们这个测试我们也知道,他并不支持排队, 

信号捕捉函数,这个信号捕捉函数咱们其实 讲一个,signal函数 下面讲另外一个sigaction函数

这个signal和sigaction到底有什么区别?我给你查一下,这个手册上怎么说的,

这个函数的行为,在 unix 版本 和历史上 不同的linux版本上他表现的行为是不一样的,避免他的使用 使用sigaction(2) 来代替它,有替代方法,

The signals SIGKILL and sIGsTopacannot be caught or ignored

这两个信号是不是不能够被捕获,和需要忽略,这个咱们 man  signal 是不是也能看到啊?所以这里像这样的东西,你就得把他们当作一个常识,这样的东西你就该记了,

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

signum 信号的编号,

sigaction *act,是一个传入的编号,很显然 const标识的参数一定是一个传入参数,因为它只能传入 不能改,改不了, 后面这个struct sigaction *oldact 又出现一个old了吧?我给大家讲过两个old 了吧?这个old是什么?是不是先前的设置吧?不关心就传null 就可以了吧?

如果你想恢复原状 那么你把它的值保存起来,用完之后是不是再设置回去啊?这个重点关注的是第二个参数,其他的都好说,当然第二个参数第三个参数 是不是都是同一个结构体啊?

 struct sigaction  第一个很明显 和signal那两个参数是不是一样啊? 很显然 这个是一个回调函数啊?大家注意 你们原来学c语言是不是讲过回调函数啊? 回调函数大多数 都不是你调用的  是谁调用的?是别人调用的,但是实现是由你来实现的,也就是说 让别人按照我的方式来执行,

 void sa sigaction)(int 也是一个信号处理函数,大家注意, 这个基本上不用,查到了没人用过,所以你就不用关心它,你就把一个信号处理函数的函数名放到这里就可以了,

void(*sa handler)(int); // 信号处理函数 

sigset t sa_mask; 你看到这个mask你应该想到信号阻塞信号集的意思,信号处理函数执行期间 需要阻塞的信号,这个怎么理解?什么叫信号处理函数执行期间需要阻塞的信号 

我们这个是不是信号处理函数啊? 如果说这个信号处理函数执行时间比较长, 假如说这里面我写个sleep5,在sleep5期间 又有信号产生怎么办?只要这个信号发生,这个信号处理函数是不是就被执行啊?那么执行期间它sleep5了,这个时候呢,你再产生信号是不是需要被阻塞,那么就需要这个参数来设置,sigset t sa_mask/  比如说我这个sigint信号 在处理期间,我需要将sigcreat 信号阻塞,我这个sigint信号 处理函数执行期间 我要将sigcreat信号阻塞,这是可以的,这个阻塞并没有涉及到内核,那么涉及到内核 必须调用sigprocmask啊?但是它并没有调用 sigprocmask 只是临时阻塞一下,那么当这个信号处理函数执行完以后呢,那个信号还是需要被处理的,只是临时的意思,

int  sa flags;//通常为 0,表示使用默认标识 一般情况下, 我们用(*sa handler)(int); 

和sigset t sa mask;如果说你在执行期间,不需要阻塞任何信号,那么这个sa mask 你就给他调用

sigmckset清空一下就可以了 清空一下,是不是这个sa mask是不是所有的位全是0啊?所有的位全是0,表示不阻塞 任何信号,但是你用的时候你不要不初始化,也不要不赋值,你不赋值它有可能是一个随机值,

因为我们这个变量是定义在栈上啊?定义在栈空间上的变量如果你不初始化 它应该是一个随机值吧?那么定义在这个全局数据区呢?是不是默认初始化位0 啊?static 是不是默认初始化也为0啊? 那么我们一般情况下 栈上定义一个指针的话,如果说你一开始不用的话是不是先等于null 啊?你不要这个 也不初始化,直接使用肯定不行,

void *sa restorer)(void); 这个标记已经不再使用了,废弃了,所以对于这个结构体来说 我们一共 第一个,sigset t
sa mask; 和int sa flags;你把这三个设置一下就可以了

这里面还有一点,说一下,你先可以思考一下
咱们先提出一个问题,那么我这个信号处理函数本身,比如说我这个sigint信号,这个信号本身,我在执行期间,如果说这个时候又产生信号了,会怎么样?当然前提我也没设置sa mask; 反正你知道一个结论 是不支持排队,

这个函数介绍 其实就是在介绍一个结构体吧?前提都是大白话,
咱们捕获一下sigint信号处理函数,要想调用这个函数,首先定义一个结构体啊?接下来需要赋值了,act.sa_handler  是不是需要处理函数啊?叫sa_handler 这个函数的参数是不是也是一个int类型?这个和signo是不是一样的?
act.sa_handler  = 这个写函数名叫sighandler 
act.sa_mask 
sa_mask 信号处理函数执行期间 你要阻塞的信号啊?在这呢 你先不阻塞哪些信号呢?在这里我们先清空一下就可以了,sigemptyset(act.sa_mask);
它是一个指针,需要阻塞的信号,
如果你要阻塞某个信号,你就把某个信号加入到这个集合当中来,还是siganset
第三个act.sa_flags=0;

接下来我们开辟一个sigaction函数,第一个是SIGINT
第二个&act 
第三个 null


接下来是不是我们应该编写信号处理函数了吧?
sighandler(int signo)
写个while1 语句循环让他不退出,sleep1
这样可以节省cpu资源,你这样空转 你可以查看一下你那个cpu利用路看多少了,肯定很快

top命令可以看cpu  僵尸进程的个数
反正你这个只要用了while 循环之后呢,这个值马上就上来了,你们也可以试一下  
怎么验证,首先你起来以后吧?你起来以后是不是你要产生一个信号吧?按ctrl+c产生信号,之后这个函数是不是会不会执行啊?这个是你需要第一验证的,第二个验证呢?假如说 sleep3 在这个 sleep3 期间 我多次按下ctrl+c 那么你就看一下  这个有没有被中断
如果说被中断了,按理说 这个sleep是不是立刻会解除阻塞啊?sleep当前是不是在这停住了 按理说它如果说你这一个信号处理函数 在执行期间呢,

 如果有一个新的信号产生了,还是它自己,如果说它被执行的话,那么这句话应该立刻打出来,那么你就看一下它有没有立刻打出来,
ctrl+c 按多次 你看有没有立刻打出来啊?它是不是又进入到这里来了?这个sigon=2没有立刻打印出来啊?
这个通过我们测试,那么你就得到一个什么结论啊?第一个结论 就是关于

按ctrl+c 是不是产生了?不是不理会,只是说执行一次,没有说不理会吧?
其实你应该能想到,你这个未决信号集当中是不是只能是记录仪的状态啊?你不管产生多少次 是不是只能执行一次啊?
这个其实咱们看那个图 也能看得出来啊,它并没有记录这个信号产生多少次吧?它没记录它怎么知道执行几次啊?

再执行一次 按ctrl+c 接下来他是不是进入到sleep这来了?很多次,也就是在这个信号函数执行期间,如果该信号再次产生,这个信号只被处理一次吧?
是不是只处理一次啊?
则信号处理函数不会被打断,当信号处理函数执行完以后,后面那些多次产生的信号,只会被处理一次,
言外之意,信号不支持排队

不能说它这个,比如说这个我们后来又产生的信号,这个信号,不能说它被忽略了,事实呢,它没有被忽略,是不是,它在这个未决信号集里面是不是保留了一个位置啊?
最后的话 是不是只能执行一次啊?

下面呢,再验证一个,假如说 我们把这个sa_mask设置一个值,我把这个sigction项加入到这个action_mask里面来,那么言外之意,我是想这么干,在信号处理函数之前呢?在信号函数处理期间,我想让阻塞sigquit信号
怎么阻塞呢? 
在信号处理函数执行期间阻塞SIGQUIT信号
我验证怎么验证呢?是不是我首先产生一个SIGINT信号吧?产生这个信号之后,是不是他会调用信号处理函数了?那么执行这个处理函数是不是在这sleep3了?sleep3期间我赶紧产生SIGQUIT信号 你看看他是不是会立刻打印这个来12
那么SIGQUIT信号是3号信号啊?你看他这里是否打印出3来,如果

 立刻打印出来了,那就意味着 我们这个信号处理函数在执行期间,被SIGQUIT信号给打乱了吧?
这个函数用signal代替吧?你得注册一下,否者的话呢,那也就是说这个信号处理函数完成以后,是不是这个信号还是需要被处理?
产生SIGINT信号,是不是没有立刻打印出来啊?
当我这个2号信号执行完以后,是不是是不是这个signo==[3]信号被处理了?
是不是3号没有立刻打印出出来吧?是不是sleep3之后才打印出来?那么言外之意,我这个2号信号处理函数执行期间,是不是我把3号信号阻塞了,
现在你按ctrl+\好使吗?
你是不是已经捕获了3号信号了,你捕获之后,这里面并没有退出,所以你只能这样杀死了,

 其他的信号也是一样的,你随便弄,刚才我是不是把这个3号信号给阻塞了?在执行期间,如果不阻塞会怎么样?不阻塞你的进程会立刻退出,也就是说你在这个sleep3期间,你收到了一个SIGQUIT信号 22  那么他会优先去处理这个SIGQUIT信号 
这个时候SIGQUIT信号 是终止进程啊?你这个进程终止了,我给你实验是不是这样的,你在这里是不是捕获了?也就是这个信号都会执行啊?
我把这个时间放大一点,sleep5  13
我先按ctrl+c是不是这句话打印出来啊?接下来是不是sleep5了?
sleep5期间我立刻产生SIGQUIT信号信号26
接下来 他再打印的话,是不是该打印这句话了?也就是说实际上你这个s1gno== 该=几了?是不是这个3立刻打印出来了?我让你看到这个,因为你我现在已经把这句话给注掉了?要反复对比

 按一下是不是有了?他是不是立刻就打印出来了? 这就是阻塞和不阻塞的区别

 

那么其他信号是不是也类似啊?
你说这个yyy信号产生了一次 执行几次啊? 是不是也执行一次啊?
若处理期间,收到了yyy信号,则yyy信号会被阻塞,暂时被阻塞, 当xxx信号处理函数执行完毕后,则yyy信号只会被处理一次,执行一次 处理一次 是一个意思 ,

本身你这个xxx信号处理期间呢,如果说这个action再次产生了,默认他就是被阻塞的,这个咱试过了吧?这个你不用设置icmark也可以,

 

6SIGCHLD 信号
这个信号呢,是咱们今天讲的一个重点,那么这个信号是干什么用的?
SIGCHLD 是父进程 利用这个信号来完成对子进程的回收,那么关于对子进程回收这一块前面是不是说过好几次了?现在呢 咱们利用了SIGCHLD信号以后呢,那么咱们这个代码就比较高级了,这种用法才是在工作中用到的方法,
咱们前面讲的只是为了讲函数而讲函数
现在这个就接近于实际的用法了
这个SIGCHLD信号是在什么条件产生的?
第一个是子进程结束的时候 很明显这个子进程退出的时候,是不是内核会给他的父进程,这个子进程的父进程,发送的一个信号,是不是我前面给大家讲的,父子进程说过了,每一个进程都有父进程,是不是大不了最后
是不是1号进程是所有的父进程啊? 1号进程是僵尸进程包括,是孤儿进程的父进程吧?反正如果说你某一个子进程他没有父进程,最后肯定是被1号进程领养的吧?

第二个子进程收到 SIGSTOP 信号 那么 SIGSTOP是不是可以使一个子进程暂停啊?暂停 停止 并不是终止的意思,在这个咱们数据里面终止是真正的退出了,暂停是不是只是说这个进程暂时不执行了?
第三个当子进程停止时,一开始,比如说你让他停止了SIGSTOP,后来你让他继续执行,是不是发出这个SIGCONT信号了?
这俩是不是先执行 SIGSTOP再执行SIGCONT啊?
不会产生这个SIGCHLD信号,那么是不是这样的?咱们给大家做一个实验。验证一下
  
这个咱们需要写一个例子,有一个fork1.c
改造一下
把这三个信号验证一下
是不是他的父进程会收到一个SIGCHLD信号
 

 一个父进程 一个子进程 这样好验证,
这样我就一个父进程 一个子进程 
你得让这个父进程 子进程都不退出才行吧?不能立刻退出吧?

 你这个父进程是不是也不能死啊?现在呢我把这个案例写好了,

 咱们这个子进程是不是能看到啊?子进程是不是这个4130啊?

 

 然后我kill-STOP 你发STOP也可以这么发,你发那个数字也行,我们还需要捕获一下,这个SIGCHLD是多少号啊?那么你打印一下是不是17就知道了

 

 

 

 ps -ef
回车,现在这个进程没死,是不是暂停了?
KILL -CONT 4146
这三种情况是不是都可以产生这个SIGCHLD信号啊?这个信号是内核给他的父亲发的,
那咱们先来看一下这个信号的作用,很明显,父进程收到这个信号之后,是不是应该去回收子进程啊?所以说这个函数的作用就是干这个的,子进程退出后,内核会给它的父进程发送 SIGCHLD 信号,父进程收到这个信号后可以完成对子进程进行回收。
如果说咱们使用了这个信号之后呢,那么我这个父进程呢是不是就可以边执行以下操作,他是不是可以做自己的事情啊?然后当他在执行自己操作的过程当中啊,收到这么一个信号,然后他 就处理这个信号,所谓的处理就是完成子进程回收

怎么完成,是不是调用waitpid 完成回收啊?或者wait等等,完成回收就可以了,后面的东西和咱们前面讲过的类似了?

  下面咱们就写一个这样一个例子,咱们还是在原来的创建基础之上,进行修改,创建3个子进程,然后父进程使用信号完成对它的回收,

 cp fork1.c sigchld.c 

这个就是一个父进程创建3个子进程,咱们就在这个基础上去使用它就可以了, 我们要注册一个信号处理函数,这个注册 应该放到父进程 完成对子进程回收应该写3这个地方,

注册SIGCHLD信号处理函数 注册的话用两个函数吧?

回收子进程用waitchild这个函数,

 这个我们在处理期间不屏蔽任何信号,

 注册完成之后,你说你这个父进程是不是应该不退出啊?你退出之后还有用吗?
是不是写while1啊?这个while1里面是不是就相当于你可以做其他事情啊?

 接下来怎么办?接下来开始写信号处理函数,SIGCHLD
在这函数里面调用wait函数,需要定义一个变量,等待任一子进程,你在这里没法获取pid 你只能写-1,WNOHANG 如果我这写0可以吗?这个函数什么情况下会调用啊?只要有子进程退出这个函数是不是马上会调用吧,是不是你这儿写0 也行啊?肯定不阻塞吧?因为这个函数只要被调用了肯定是有子进程死掉的吧?当然前提是因为那两个条件是不是也可以产生信号的啊?
sigcond  sigstem 你发那是不是在这就堵死了?最好是写WNOHANG  不要写0,
当然你写那个的话,是不是相当于子进程还没死呢?在这最好写这个WNOHANG  如果不考虑那个两种情况,你这个写这个和写零是不是一样的?

判断一下(wpid>0) 表示回收子进程了,
if(wpid==0) 这个时候有=0的 情况吗?这个相当于子进程还活着呢

还有一个else (wpid==-1) 全部都死掉了
 

 

咱们先这样写,然后我们子进程退出的时候我先简单的测一下,这里写sleep1 
我这样写,是不是要让这三个子进程有先有后退出啊?然后你先看一下 
咱们这样写是不是能够完全回收子进程?
是不是都死掉了,这三个是不是我们重点关心的?

那么你看一下还有没有僵尸进程 啊?

 

 

 是不是没有啊?这个是不是一个父进程啊?三个子进程已经全部死掉了, 而且我们也能够正常回收了吧?这里面是不是死掉一个子进程 是不是这个函数执行一次啊?
因为我这个父进程用的就是信号吧?
我把这三个sleep全部去掉,
那么这三个子进程 全部去掉,那就意味着有可能这三个子进程,有没有可能同时退出的情况?
现在呢 这样还不是特别明显,你到时候看一下 回收几个就可以了?这个执行了几次啊?
这种就是你执行的时候它效果不容易看出来,

来个容易看出来的,我在这加sleep2  27 我sleep1 期间会发生什么?

 

 

 言外之意,我这个信号处理函数,我sleep2期间,那么其他的子进程,是不是有可能全部死掉了?
回收了2个, 那么有没有僵尸进程啊?

有一个,这个是怎么产生的, 我这个sleep2期间27  是不是另外两个子进程全部死掉了?此时全部死掉,我们这个信号处理函数是不是此时也不应该被打断啊?因为我这个信号处理函数执行期间,
信号SIGCHLD本身是不是被阻塞的?当这个信号处理函数 完成以后 是不是还会执行一次?
所以第二个子进程也被回收了?
最后还剩下一个
这个是不是完全符合逾期啊?我如果让你模拟一下 让三个子进程全部 成为僵尸进程怎么模拟?
你说有没有可能出现这种情况?
我这个信号处理函数,这个函数还没有注册完成呢83,三个子进程全部死掉了

 

 有没有可能 很有可能,我在这82加个sleep就可以了,
三个子进程已经全部起来了,而且我sleep5期间这3个子进程已经全部退出了吧?退出之后你才完成注册  还能 收到信号吗?

很显然不能,下面应该有3个僵尸进程, 

是不是正好符合咱们的预期?如何解决这个问题?
存在的,哪怕只有万分之1 你也得把他解决掉,
怎么解决?
仔细想一下咱们今天讲什么了?是不是信号集啊?我能不能这样干?我先将这个SIGCHLD信号先给他阻塞,未完成注册之前,我先把SIGCHLD先给阻塞了,这个时候如果说子进程退出了,这个时候是不是内核会给内核发出一个SIGCHLD的信号啊?没有关系,这个时候信号是不是保留到未决信号集当中了?我完成注册以后,那么你说这个信号能被处理吗?

一开始,假如说我一开始还没完成注册呢,没有完成注册 之前我先把SIGCHLD先给它阻塞了
这个时候子进程退出了,退出几个?有退出的,这个信号是不是会留在这个未决信号集里面啊?然后呢我完成注册以后,我再解除阻塞,那么你这个信号能够被处理几次啊?被处理一次
接下来咱们是不是想到能处理了?如何做到收到一个信号  解决三个子进程,怎么做?

先解决未完成注册的,三个子进程先都退出,在之前呢,先将SIGCHLD信号阻塞,然后完成信号注册之后,再解除阻塞,

咱们可以在fork之前一开始先将SIGCHLD先给它阻塞掉,
将这个SIGCHLD信号加入到这个集合当中来啊?

 接下来调用s1gprocmask 40 
你是不是也可以用第三个参数恢复现场啊?

 这个是先阻塞信号吧?然后我们完成注册之后,是不是可以解除阻塞?这个是不是写到这句话下面啊89?

咱们先加入这句话,应该会产生一个现象,我sleep(5)期间是不是3个子进程全部退出了?咱们先口头分析一下,
sleep(5)期间是3个子进程全部退出了 这个时候未决信号集里面应该保留一个位置,那么这个SIGCHLD应该会保留这个未决信号集里面,接下来 我们解除阻塞之后,是不是这个SIGCHLD就会被处理啊92?
接下来它是不是要调用这个函数10-26?

 这个函数它调用了几次?是不是回收了一个?按理说  回收一个还剩两个僵尸进程,

 

 两个僵尸进程,我们可以收到一次信号,回收3个,收到一个信号,这一个信号只是告诉父进程 它可以去回收了,回收几个 要看具体有几个,正常情况下,我们在信号处理函数执行期间,有没有可能有多个子进程同时退出?比如说有10个子进程,第一个子进程退出了,正在处理期间,后面五六个子进程全部一下就退出了,这个时候未决信号集里面是不是只有一个位置啊?那么即便这样的话,你最后再回收是不是也只能回收一个啊?所以你只能在一个循环里面是否收到一个信号,然后完成多个回收,
写在一个while循环里面 就这点来说是不是和咱们前面讲过的基本类似了?,

 我这样做以后会达到一个什么样的效果啊?
即便是三个子进程全部退出了,咱们先给你分析一下,一种极端情况,你三个子进程全部退出了,我才完成对信号的注册吧?后来 我解除阻塞之后,是不是会收到一个信号啊?肯定有一个信号吧?因为信号有一个位置吧?这个时候它是不是调用这个函数 进行回收吧?一个while循环把3个是不是全部都回收了?
如果说信号处理函数在执行期间,由其他的两个子进程也退出了,还用给信号吗?是不是顺便你
你本身信号处理函数执行期间,你再次有子进程退出的话 是不是也不理会他啊?因为那些信号是被阻塞了?暂时被阻塞了,它是不是在一个whil循环里面也回收了? 你不管是有几个,一个也好,那么按顺序来呢,一个一个退出,这句话打印出来之后,18 -21它再循环一次,立刻,是不是它子进程还活着?这里不应该这么写了 应该break;25  
还有一个wpid=-1没有子进程了,你也要break 27 -30
这两个可以合在一块

 即便是你一个一个退出,还是说一开始,有一个退出,后来有两个全部退出,同时退出,或者是你三个子进程全部退出,同时退出的意思,这个都能解决15-33,

 把这里面的所有sleep全部都 去掉,去掉之后是不是很快就退出了?

 也就是这个while循环肯定会多循环一次啊?现在是退出来了,它是不是应该在这个-1这有break啊30? 必须有break 你如果不写break是不是怎么样?进入死循环了,进入死循环以后,这句话会29不停的打印


有可能还未完成信号处理函数的注册三个子进程都退出了

解决办法: 可以在 fork 之前先将 SIGCHLD 信号阻塞,当完成信号注册以后呢,再解除阻塞


如果说像这种情况呢,就是说你完成信号处理函数注册之前呢,三个子进程全部结束了,然后完成注册之后,你再解除阻塞,其实会收到一个信号,但是完成3个子进程回收,
当 SIGCHLD 信号函数处理期间,SIGCHLD 信号若再次产生是被阻塞的
前面讲了吧 sigaction前面是不是给你说了,还有sigx信号,某个信号,也就是信号本身,他的信号处理函数执行期间,他自己的信号是被阻塞的,那么这种情况他适合于哪一种情况啊?假如说我有一个子进程退出了,而且我这个父进程正在恢复期间啊?另外两个子进程也全部一块退出了,最后是不是只能回收一个啊?如果你不写while循环的话,这种情况适合那一种,那这里解决办法,可以在信号处理函数里面使用 while(1)循环回收
这样的话,就可能会出现捕获一次SIGCHLD 信号但是回收了多个子进程的情况,从而可以避免产生僵尸进程。
两个红色标记的是我们今天讲的,一些细节的分析,这个你自己在代码上可以模拟模拟,

 调用wait 是不是不太好吧60?wait是一个阻塞函数,按理说只要有子进程退出了,那么调用wait 函数他肯定也不会阻塞吧?

 如果把这个信号注册函数写在fork函数写在之前,那么这些信号处理函数是不是也被子进程记录下来啊?但是记录下来之后呢,对他没有用,因为他自己是没有子进程的,之所以这样写,把之前讲过的内容是不是整个都窜进来了啊?这里面用到了信号集相关的函数,如果把一个信号加入到阻塞信号集当中,以及如何解除阻塞

 

 这个图讲的是信号的处理过程,
分为两个大块,上面是用户区,下面是内核区,
用户模式 用户模式, 用户态和内核态,都是一个意思,看一下在执行主控制流程的的某些指令,或者是因为中断,异常,系统调用进入内核 哪个函数能进入到内核?哪一类函数?一般情况下系统调用函数都会进入内核 ,比如read  wrait 也就是说这一部分是涉及到用户态到内核态的切换啊?切换到内核态里面来了,内核处理完异常
准备回用户模式之前,先处理当前进程中可以递送的信号 那么可以递送的信号在哪放着呢?是不是在未决信号集里面放着呢?
也就是说你的进程执行的时候,进入到内核态以后,他执行它原来的操作以后,他要看一下当前进程有没有要处理的信号,实际上他读的是未决信号集,如果说他看到未决信号集里面有需要被处理的信号,那么也就是说这个信号没有被阻塞吧?他就会去处理这个信号,那么他怎么处理整个信号?他是不是要调用用户自定义的void sighandler(int)回调函数,或者是忽略啊,执行默认动作是不是都可以啊?在这我们这个图上体现的什么?用户自定义的信号处理函数吧?实际上就是说这个内核调用的是,用户区的函数,他怎么知道要调用哪个函数,是不是注册的?用户注册的,用户态里面已经函数了,已经告诉内核了,如果这个信号发生了,那么执行哪个函数,那么这样的话,是不是做到这一点啊3?内核调用的是我们自己写的函数吧?
怎么做是不是我说的算?什么时候做?是不是内核说了算?也就是说这个信号是如何产生的?那么他才去会调用吧?
往往回调函数是这么干的,因为你已经把这个信号的处理函数已经告诉内核了,所以内核知道调用哪个函数, 这个处理函数完成以后,这个回调函数完成以后,他是不是要继续回到内核啊?

在这我问一下大家 他为什么回到内核?是不是函数返回是不是应该返回调用者的位置啊?谁调用的这个回调函数啊?内核调用,所以他回到了内核 
谁调用的这个函数,那么这个函数完成之后就返回到谁的位置,

举个例子,比如说main函数里面调用了prentf 他是不是调用了prentf 的时候他是不是会跑在prentf处啊,那么这个prentf 函数完成以后,是不是又回到main函数了?一定是这样的,谁调用了这个函数就回到了谁的位置,回到调用者的位置 这个是你需要理解的,接下来他是不是又回到内核了4? 回到内核是怎么回的呢?其他他调用的是sigreturn这个函数这个你不用管他这个,反正你知道他是回到内核了就可以了,那么回到内核以后 他是不是继续往下走啊5?继续往哪走啊?
这个内核是怎么进来的?一开始怎么进来的?是不是中断 异常 系统调用才会进来的?接下来他是不是又返回调用者的位置啊?
调用者是哪啊?是不是用户态啊?那肯定是从用户态开始的嘛这还用说?回到了用户态,也就是说又回到了中断发生的地方,或者你调用read wrait 这些回调函数这些系统函数 是不是也可以啊?总之一定是回到中断那个位置,

5.返回用户模式从主控制流程中上次被中断的地方继续向下执行 是不是回到这个位置啊1?

从这里面有一点隐含了一点,这个信号是不是有一定的延时性啊?内核处理完准备回用户模式之前先处理当前进程中可以递送的信号2,言外之意他是不是一开始就没有处理这个信号啊?
实际上这里面这句话你要注意,要想处理这个信号呢,必须能够进入内核,如果进入不了内核这些信号暂时处理不了,其实总是有办法可以进入内核的,最常用的办法是不是调用printf 能不能进入内核?read wrait 是不是也可以?很多函数都可以进入内核,因为咱们很多写了这个用户的这个函数呢,比如说c++库函数,内部很多调用的是系统函数啊?
系统函数很多都可以进入内核,比如说io操作是不是都可以进入内核?
那么这个里面总之你记住一句话就可以了,哪一句话?
谁调用了这个回调函数,他就回到谁的位置
很显然是内核调用的,内核调用,那么这个信号处理完成以后呢他还返回内核,这个时候内核再继续往上反 返回到哪呢?那就看他从哪开始中断的?从什么情况下他进入到内核,那么他就返回到哪,这个函数的调用 是不是他最终要压栈啊?
那么你压栈 是不是你这个函数完成之后 是不是你要弹栈啊 
弹弹哪去了,是不是弹到调用者的位置了?回到调用者的位置,
 

这个信号处理函数完成以后呢,他回到的一定是调用者的位置,而且你 得知道这个信号有一定的延时性,

不是说你这个信号产生了,他会去立刻调用这个信号处理函数,那么其中有一条叫什么?必须能够进入内核
咱们说这个程序是不是很多都可以进入内核啊?

最简单的printf 肯定可以进入内核,
sleep1能够进入内核吗?肯定可以 
sleep1里面调用了能够进入内核的函数 我记得好像是alarm 
这个信号处理过程 我就说这么多
你把这个图 结合这个文字你多读两边,不用花太多的时间搞
这个玩意
把  SIGCHLD signal 一些常用函数 搞一搞

上班的时候这些东西完全够你用,而且还是讲的比较深的
 

这个信号是不是也是进程间通讯的一种手段啊?
这些都是进程间通讯的一种手段,但是不要使用信号完成进程间通讯,一般我们使用信号就是在一个进程里面来注册一个信号处理函数,当然这个信号发生之后我们去怎么做?一般这么干,

很少说有人说在两个进程之间使用信号来完成通讯的,没有人这么干 你也不要这么干,因为信号 其实你要说他复杂 也复杂  你要说他简单也简单,那么得看你怎么用

在这呢咱们有一个例子给你说一下 使用信号完全可以完成两个进程之间通讯,他是可以做到的,
有个数数的例子,使用的是
SIGUSR1和SIGUSR2这两个信号完成父子进程间交替数数 子进程 他数0 他数1  他数2  他数 3
是不是交替啊 这样来数

 

 先看一下效果,

 

 

 

 因为正常情况下,没有使用 信号完成进程间通讯的,
这个你知道怎么用就可以了
怎么给一个进程发送信号的?
是不是kill 啊?咱们是不是讲了很多种信号啊?那么很多种信号你是不能乱用的,有很多信号,你很多信号你不能用kill的你乱发,那么你比如说你这个信号 SIGINT 是不是按ctrl+c才会产生啊?你不是说你给父进程发送一个kill 这玩意吧?
因为这些信号是在特定条件下才会产生的,所以这些信号是不能乱用的,能够让你使用的是这两个SIGUSR1和SIGUSR2 这两个是用户自定义信号,你可以用,这里面用的就是这两个信号,
咱们看一看这个函数是怎么写的?

 man 函数fork一个子进程,然后有两个回调函数,
分别是父进程的信号处理函数和子进程的信号处理函数,先看父进程 num=0

 num是不是从0开始数啊?我数数的时候就数的num 父进程是不是先数的,刚刚看的时候,
 flag =1相当于一个开关,接着看一会你就知道什么意思了,

接下来调用的45 signal 是不是我注册了一个信号处理函数啊?注册SIGUSR1 这个信号处理函数, 接着看他的信号处理函数是不是func1啊?
接下来他进入一个while 循环,if(flag==0) 49
flag一开始是不是不等于0 啊? 一开始等于1  这个49是不是不满足条件啊?不满足条件是不是相当于在这做 while循环啊。如果满足条件他会kill(pid,SIGUSR2),给谁发送SIGUSR2信号啊?是不是给子进程啊?这个pid是不是子进程的pid啊?他给子进程发送一个SIGUSR2信号,同时他自己把flag =1了52,
置为1之后,这个49-53 他是不是就不在执行了吧?
这两个开关一会开一会关是不是交替执行啊?

子进程是这么写的

子进程是num=1,flag =0 然后注册了一个信号处理函数,signal(SIGUSR2, func2);然后信号处理函数是func2
接下来进入while循环 if(flag==0) 他一开始=0,满足条件,66kill是不是就执行了?他是不是给他的父进程发送SIGUSR1 信号,发送这个信号之后flag=1
这个是不是又不满足了,程序的入口就在这,一开始是不是就发送了吧? 是不是父进程就执行这个func2函数了

他给子进程发送的吧?SIGUSR1是子进程, 给父进程吧?父进程的信号处理函数 是不是func1啊?这个就开始执行了,
所以16这个一开始就打印出来,
因为父进程这个一开始=0啊43?
所以这个0打印出来,他是不是加了2吧 因为他是交替数嘛,他下一次数肯定是2
这个2打印出来,然后flag=0,是不是相当于开关打开了,那么父进程里面这个flag=0,接下来继续看,是不是这个49-53这个条件满足了,
这个是父进程,这个条件满足了,是不是他开始给子进程发啊?给子进程发以后,那么你说这个子进程会执行哪个函数呢?
是不是执行22-28这个函数?
子进程这个初始值是几啊?num=1,所以这个1也打印出来了,应该说他是不是也+=2啊? 他也flag=0 然后sleep=1,
在这为什么sleep1 啊?不sleep会怎么样?数数太快了,你观察不了,
这个子进程(flag==0)是不是又开始满足条件了吧?64-68 是不是又开始给父进程发啊?
因此他俩来回的给对方发,通过这个flag开关 是不是一会打开一会关闭,是不是这样交替数数啊?
这个代码你看一看  你知道怎么回事就可以了,
 

这个题目考到了,你把这个代码看明白了 麻烦的就这里面的逻辑你可能得需要琢磨琢磨
 

这个用flag开关来回的控制啊?一会开一会关,开关在哪控制的呢?是不是信号处理函数里面控制的?

你把这代码不要求你写出来你先把他看明白

一般情况下,我们也不会使用信号完成两个进程间通讯,一般也没有这么用的,一般我们用这个信号,就这么简单 signo,sigaction
然后写一个信号处理函数,那么这个信号处理函数里面干什么事呢?比如说完成对数据库断开连接,比如说释放资源 等等

比如说收到一些异常信号了,可以退出进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值