一、环境变量
二、文件输入重定向
三、管道(无名管道、有名管道)
四、信号的基础
作业提醒:
100~200之间的素数
编写一个函数,实现判断一个数是否是素数?
机制 策略
今天内容:
一、信号阻塞和未决信号
进程可以设置对某个信号的阻塞(屏蔽)。
需要使用到sigset_t(信号集)数据类型
关于对sigset_t数据类型的操作函数有以下。
sigemptyset(3)
#include <signal.h>
int sigemptyset(sigset_t *set);
功能:将信号集清空
参数:
set:指定要清空的信号集
返回值:
0 成功
-1 错误
int sigfillset(sigset_t *set);
功能:将信号集设置为满
参数:
set:指定要置满的信号集
返回值:
0 成功
-1 错误
int sigaddset(sigset_t *set, int signum);
功能:给信号集添加信号
参数:
set:指定信号集
signum:指定要添加的信号
返回值:
0 成功
-1 错误
int sigdelset(sigset_t *set, int signum);
功能:删除信号集中的某个信号
参数:
set:指定信号集
signum:指定要删除的信号
返回值:
0 成功
-1 错误
int sigismember(const sigset_t *set,int signum);
功能:测试信号是否是信号集中的一个成员
参数:
set:指定的信号集
signum:指定信号
返回值:
-1 错误
0 信号不是信号集中的一个成员
1 信号集中有这个信号
希望设置进程对2号信号阻塞,需要如何处置阻塞信号集。
1、sigset_t block_set;
2、将block_set集合里的所有成员清空
3、将2号信号添加到block_set集合中
4、将block_set设置为这个进程的阻塞信号集。
要完成第四步需要使用到函数sigprocmask(2)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, \
sigset_t *oldset);
功能:检测或改变阻塞信号
参数:
how:
SIG_BLOCK 当前信号集和set指定信号集的并集
SIG_UNBLOCK 将set集合里的成员从当前进程阻塞信号集中移除
SIG_SETMASK 将set指定为当前进程的阻塞信号集
set:指定的新的信号集
oldset:之前的信号集保存到oldset中。
返回值:
0 成功
-1 错误
编写代码实现进程对2号信号的阻塞。代码参见blocked.c
1、解除对相应信号阻塞的时候会是什么情况?
修改blocked.c,察看解除对2号信号阻塞的时候,发生的情况。做总结。
可靠信号 没有信号丢失的信号成为可靠信号 34~64.
不可靠信号 有信号丢失的情况,这种信号为不可靠信号
1~31
2、在信号阻塞期间,可以察看未决信号。
如何查看未决信号。使用sigpending(2)查看
#include <signal.h>
int sigpending(sigset_t *set);
功能:检测未决信号
参数:
set:值-结果参数,用于返回进程的未决信号掩码
返回值:
0 成功
-1 错误
举例验证,使用sigpending检测当前进程的未决信号。
代码参见 pending.c
二、pause(2)的使用
#include <unistd.h>
int pause(void);
功能:等待一个信号
参数:
void
返回值:
-1 错误 errno被设置为EINTR
举例验证pause(2)的使用 代码参见pause.c
使用alarm(2)和pause(2)完成sleep的功能,代码参见mysleep.c
三、信号传送处理过程
1、用户输入命令,在bash下启动一个前台作业。
2、用户按下ctrl+c键,这个键盘输入产生一个硬件中断。
3、如果CPU正在执行这个进程的代码,则该进程的用户空间代码暂停执行。CPU从用户态切换到内核态处理硬件中断。
4、终端驱动程序将ctrl+c解释成一个SIGINT信号,记录在该进程的PCB中。
5、当某个时刻,进程从内核态切换会用户态的时候,首先处理PCB中记录的信号,如果PCB中有未处理信号,找到信号对应的信号处理程序进行处理。
6、信号处理函数处理完毕,调用sigreturn(2),继续返回到进程的内核态。再次循环到第五步。
四、可重入函数
函数使用到的变量的空间全部分配在栈桢中,那这样的函数称为可重入函数。否则称为不可重入函数。
信号的处理函数尽量保证为可重入函数。
举例验证可重入函数的重要性。代码参见 reenterable.c
五、编写代码验证信号处理函数的继承
验证子进程是否继承父进程的信号处理函数。代码参见p_signal.c
子进程继承了父进程的信号处理函数。
信号属于进程的资源,在进程的PCB中有信号的记录。
六、setitimer实现定时器
系统计时器
运行一个进程的时候,进程所消耗的时间包括三个部分:
用户时间:进程消耗在用户态的时间
内核时间:进程消耗在内核态的时间
睡眠时间:进程消耗在等待I/O、睡眠等不被调度的时间
内核为每个进程维护三个计时器
真实计时器:统计进程的执行时间
虚拟计时器:统计进程的用户时间
实用计时器:统计进程的用户时间和内核时间之和。
执行时间=用户时间+内核时间+睡眠时间
这三个计时器除了统计进程的各种时间以外,还可以按照各自的计时规则,以定时器的方式工作,向进程周期性的发送不同的信号。
SIGALRM 真实定时器
SIGVTALRM 虚拟定时器
SIGPROF 实用定时器
通过使用setitimer(2)设置、启动、关闭定时器
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置一个间隔时间的定时器
参数:
which:
ITIMER_REAL:SIGALRM
ITIMER_VIRTUAL:SIGVTALRM
ITIMER_PROF:SIGPROF
new_value:定时器新值
old_value:定时器原来的值
返回值:
0 成功
-1 错误 errno被设置
补充:
struct itimerval{
/*间隔时间*/
struct timeval it_interval; /* next value */
/*初值时间*/
struct timeval it_value; /* current value */
};
struct timeval{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
举例验证setitimer(2)的使用。代码参见 timer.c
七、前台作业和后台作业
什么是作业?
day10$ls -l|grep a.out
-rwxrwxr-x 1 tarena tarena 7336 3月 27 16:27 a.out
将前台运行的作业切换到后台停止。ctrl+z
day10$gcc signal.c -o h
day10$h
^C^\recv signal...
^Z
[1]+ 已停止 h
如何查看后台作业?
day10$jobs
[1]+ 已停止 h
将后台作业切换到前台作业
day10$fg %1
h
^C^C^C^C^C^\recv signal...
^\recv signal...
^\recv signal...
^\recv signal...
^\recv signal...
让停止的作业在后台运行
day10$jobs;bg %1;jobs;
[1]+ 已停止 h
[1]+ h &
[1]+ 运行中 h &
前台作业可以使用快捷键发送信号杀死,但是后台作业不可以。
启动进程的时候,可以然作业直接到后台运行
day10$h &
[1] 5418
总结:
一、信号阻塞和信号未决
二、信号被子进程继承
三、信号的产生和处理的过程
四、可重入函数
五、pause(2)函数的使用
六、setitimer(2)实现定时器
七、前台作业和后台作业。
作业:
将文件重定向的功能加入到mysh代码中。