两个进程间想通讯,必须要通过内核,今天讲的信号其实本质也是讲进程间通讯的意思,那么你为什么可以在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 你自己随便用