一、进程的管理
1.1 进程管理的其他函数
(1)vfork()
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
功能:
- 该函数类似于fork(),都是创建当前正在调用进程的子进程,有关细节、返回值以及可能出错的原因参考fork();
- 该函数是一种特殊的克隆,在创建新的进程时不会去复制父进程中的页表信息(内存空间等);
- 该函数创建完毕子进程后会挂起父进程,直到子进程终止或者调用exec系列函数为止,也就是保证了子进程先执行;
- 在子进程终止/调用exec系列函数之前,子进程共享所有内存空间和父进程,包括栈区;
- 子进程不能从当前函数中返回,也不能调用exit(),但是可以调用_exit();
(2)exec系列函数
#include <unistd.h>
int execl(const char *path, const char *arg, .../* (char *) NULL */);
功能:
主要用于执行参数指定的文件指令;
参数:
第一个参数:字符串形式的路径名;
第二个参数:字符串形式的参数,一般指定具体的执行方式;
第三个参数:可变长参数;
返回值:
success —- 无返回值,error —- -1;
如:
使用execl()执行ls -l命令,执行方式如下:
execl("/bin/ls/","ls", "-l", NULL);
注意:
vfork()本身没有太大的实际意义,一般与ecec系列的函数搭配使用,主要用于子进程执行与父进程完全不同代码段的场合中;
其中vfork()专门用于创建子进程,ecec系列函数专门用于跳转到全新的代码段中;
使用fork()也是可以与exec系列的函数进行搭配使用,但是效率没有vfork()高,但是最新的fork()内部由采用了写时拷贝技术来提高fork()的效率;
(3)system()
#include <stdlib.h>
int system(const char *command);
功能:
主要用于执行参数指定的shell命令,成功返回命令的退出状态信息,失败返回-1;
二、中断的概念和分类
(1)基本概念
中断本质上就是停止当前程序的执行转而去执行新的程序或者处理意外情况的过程;
(2)基本分类
硬件中断、软件中断;
三、信号的处理
3.1 基本概念和特性
(1)基本概念
信号本质上就是一种软件中断,它既可以作为两个进程间通信的一种机制,更重要的是,信号总是可以终止一个程序的执行,它更多地被用于处理意外情况;
(2)基本特性
a. 信号是异步的,也就是说进程并不知道信号何时会到来;
b. 进程既可以发送信号,也可以处理信号;
c. 每个信号都有一个名字,用SIG开头;
3.2 信号的基本命令和分类
(1)基本命令
kill -l
:表示查看当前系统所支持的所有信号;
要求掌握的信号:
SIGINT | 信号2 | 使用ctrl+c来产生该信号 | 默认终止进程 |
SIGQUIT | 信号3 | 使用ctrl+\来产生该信号 | 默认终止进程 |
SIGKILL | 信号9 | 使用kill -9来产生该信号 | 默认终止进程 |
(2)基本分类
在当前教学系统中所支持的信号范围是:1~64,不保证是连续的;
其中1~31之间的信号叫做不可靠信号,不支持排队,信号可能会丢失,也叫做非实时信号;
其中34~64之间的信号叫做可靠信号,支持排队,信号不会丢失,也叫做实时信号;
3.3 信号的处理方式
(1)处理方式
- 默认处理,绝大多数信号的默认处理方式都是终止进程;
- 忽略处理;
- 自定义处理 / 捕获处理;
(2)相关函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数原型解析如下:
typedef void (*sighandler_t)(int);
=> typedef void (*)(int) sighandler_t;
sighandler_t signal(int signum, sighandler_t handler);
=> void (*)(int) signal(int signum, void (*)(int) handler);
=> void (*)(int) signal(int signum, void (*handler)(int) );
=> void (*signal(int signum, void (*handler)(int) ) )(int);
=> signal首先是一个函数;
该函数有两个形参,一个是int类型,一个是函数指针类型;
该函数的返回值类型是一个函数指针类型
=>上述两个函数指针都是一个指向参数为int,返回值类型为void的函数的指针;
-
函数功能解析如下:
-
功能:
主要用于设置指定信号的处理方式;
参数:
第一个参数:具体的信号值 / 信号名称;
第二个参数:具体的处理方式;
SIG_IGN - - - - - - - - - 忽略处理(ignore)
SIG_DFL - - - - - - - - - 默认处理(default)
自定义函数的地址 - 自定义处理
返回值:
success —- 之前的处理方式,error —- SIG_ERR;
注意:
SIGKILL这个信号只能采用默认处理方式进行处理,不能被用户捕捉,也就是不能用signal()去设置该信号的处理方式
3.4 父子进程对信号处理方式的比较
(1)对于fork()创建的子进程来说,完全照搬父进程对信号的处理方式,父进程默认,子进程也默认处理;父进程忽略处理,则子进程也忽略处理;父进程自定义处理,子进程也自定义处理;
(2)对于vfork()和execl()启动的子进程来说,父进程忽略,子进程也忽略;父进程默认,子进程也默认;但是父进程自定义,子进程却默认处理;
3.5 发送信号的主要方式
(1)采用键盘发送信号(只能发送部分特殊的信号)
ctrl + c => 可以发送信号SIGINT 2
ctrl + \ => 可以发送信号SIGQUIT 3
… …
(2)程序出错发送信号(只能发送部分特殊的信号)
段错误 => 可以发送信号SIGSEGV 11
(3)使用kill命令发送信号(所有信号都可以发送)
kill -信号值 / 信号名称 进程号
=> 表示给指定的进程发送指定的信号
(4)采用系统函数发送信号
kill() / raise() / alarm() / …
3.6 发送信号系统函数的解析
(1)kill()
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
-
功能:
- 主要用于给参数指定的进程发送参数指定的信号; 参数:
-
第一个参数:进程的编号;
>0 - - - - 表示发送信号给进程号为pid的进程
0 - - - - - 表示发送信号给调用进程组中的每一个进程
-1 - - - – 表示发送信号给每一个进程P,进程1除外;(进程P必须允许调用进程发送信号给它)
<-1 - - – 表示发送信号给ID为-pid的进程组中的每一个进程
第二个参数:信号值 / 信号名称;
0 - - - - 表示不会发送信号,只是检查进程指定的进程是否存在
练习:
vi 11kill.c文件,首先使用fork()创建子进程,然后子进程开始启动,并设置对信号40进行自定义处理,进入无限循环;父进程开始启动,判断子进程是否存在,若存在则用kill()给子进程发送信号40,并观察程序的执行结果;
明日预报:
(1)信号的处理
(2)进程间的通信技术