# Wed 24 Jan 11:30:36 GMT 2018
第8章 异常控制流 (exceptional control flow)
8.1 exceptions
Exceptions are a form of excepional control flow that
are implemented partly by the hardware and partly by the
operating system.
A state is encoded in various bits and signals inside
the processor. The change in state is known as an ‘event‘.
8.1.1 exception handling
At system boot time, the operating system allocates
and initializes a jump table called an ‘exception table‘
At run time, the processor detects that an event has
occured and determines the corresponding exception number.
The processor then triggers the exception by making an
indirect procedure call to the corresponding handler.
The exception number is an index into the exception
table, whose starting address is contained in a special
CPU register called ‘the exception table base register‘
8.1.2 classes of exceptions
Exceptions can be divided into four classes:
+ Interrupts
cause: signal from I/O device
Asynchrony
return to next instruction
+ Trap
cause: intentional exception
synchrony
return to next instruction
+ Fault
cause: potentially recoverable error
synchrony
might retrun to current instruction
+ Abort
cause: Nonrecoerable error
sync
Never returns
8.1.3 linux/x86-64 系统中的异常
0 - 31 号码对应由intel 架构师定义的异常。
32 - 255 对应的是操作系统定义的中断和陷阱。
c程序用syscall函数直接调用任何系统调用。所有到linux
系统调用的参数都是通过通用寄存器传递的。按照惯例:
%rax:包含系统调用号。%rdi,%rsi,%rdx,%r10,%r8和%r9包
含最多6个参数。
从系统调用返回时,寄存器%rcx和%r11都会被破坏,%rax
包含返回值。-4095到-1之间的负数返回值表示有错误.(errno)
8.2 processes(进程)
The key abstractions that a process provides to the
application:
+ An independent logical control flow that provides
the illusion that our program has exclusive use of
the processor.
+ A private address space seems to has exclusive use
of the memory system.
8.2.1 逻辑控制流
PC值的序列叫逻辑控制流,简称逻辑流
多个流并发地执行简称‘并发’,一个进程和其他进程轮流运行
称‘多任务’。Each time period that a process executes a
portion of its flow is called a time slice(时间片), Thus,
multitasking is also refered to as time slicing(时间分片).
如果两个流并发地运行在不同的处理器核或者计算机上,那么
称为并行流(parallel flow)and running in parallel and
parallel execution.
8.2.5 context switch
操作系统内核使用一种为‘上下文切换’的较高层形式异常控制
流来实现多任务。
The kernel maintains a context for each process. The
context is the state that the kernel needs to restart a
preempted process.
内核可以决定抢占当前进程,并重新开始一个先前被抢占的
进程。这种决策叫‘调度’(scheduling)。由内核调度器代码进
行处理的(schecduler)。
上下文切换机制:
+ 保存当前进程的上下文
+ 恢复某个先前被抢占的进程被保存的上下文
+ 将控制传递给这个新恢复的进程
8.3 系统调用错误处理
使用错误处理包装函数
8.4 进程控制
Unix提供了大量从c程序中操作进程的系统调用。
8.4.1 获取进程ID
每个进程都有一个唯一的正数(非零)进程ID(pid). getpid
函数返回PID。
pid_t getpid(void);
8.4.2 creating and terminating process
程序员的角度,进程有3种状态:
1 运行. 2 停止. 3 终止.
void exit(int status);
pid_t fork(void); //子进程返回0,错误 -1
子进程可以读写父进程打开的任何文件。自己有独立的空间。
8.4.3 reaping child processes
pid_t waitpid(pid_t pid, int *statusp, int options);
#pid: > 0 is one child pid
= -1 is set of child processes
#options: 0 is default - wait child terminate
WNOHANG:
no wait for termination
WUNTRACED:
suspend until one child terminated/stop.
WCONTINUED:
suspend until terminated or until a
stopped process in the wait set has been
resumed by the receipt of a SIGCONT signal
# if the satusp is non-NULL, then waitpid encodes
status information about the child that caused the
return in status, which is the value pointed to by
statusp.
pid_t wait(int *statusp);
Calling wait(&status) is == waitpid(-1,&stutus,0).
8.4.4 putting processes to sleep
unsigned int sleep(unsigned int secs);
int pause(void); //pause until receive a signal
8.4.4 loading and running programs
int execve(const char *filename,const char *argv[],
const char *envp[]);
the execve function loads and runs the executable
object file ‘filename‘ with the argument list ‘argv‘ and
the environment variable list envp.
linux提供了几个函数来操作环境数组:
char *getenv(const char *name);
int setenv(const char *name,const char *newvalue,
int overwrite);
void unsetenv(const char *name);
8.5 signal(信号)
8.5.2 sending signals
process groups(进程组)
pid_t getpgrp(void); //return process group ID
子进程属于父进程组。
int setpgid(pid_t pid, pid_t pgid);
linux> /bin/kill -9 15213
int kill(pid_t pid, int sig);
Sending signals with the alarm function:
unsigned int alarm( unsigned int secs);
# send a SIGALRM signal to the calling process in secs.
接收信号
#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
SIG_IGN SIG_DFL
8.5.4 阻塞和解除阻塞信号
隐式阻塞:内核默认
显式阻塞:sigprocmask函数。
int sigprocmask(int how, const sigset_t *set,
sigset_t *oldset);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
# success 0, otherwise -1
int sigismember(const sigset_t *set, int signum);
arg ‘how‘:
SIG_BLOCK: 添加信号到block set
SIG_UNBLOCK: 从set中删除
SIG_SETMASK: block = set
if oldset is non-NULL,the previous value of the blocked
bit vector is stored in oldset.
安全和函数:
+ 可重入的(例如只访问局部变量)
+ 不能被信号处理程序中断
# man 7 signal 可查找具体安全函数
# 信号处理程序产生输出‘唯一’安全的方法是使用write函数。
portable signal handling
Posix standard defines sigaction function:
int sigaction(int signum, struct sigaction *act,
struct sigaction *oldcat);
handler_t *Signal(int signum, handler_t *handler)
{
struct sigaction action,old_action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags =SA_RESTART;
if (sigaction(signum,&ation,&old_action) < 0)
unix_error("signal error");
return (old_action.sa_handler);
}
#include <signal.h>
int sigsuspend(const sigset_t *mask);
非本地跳转是通过setjmp和longjmp函数来提供的。
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
# include <setjmp.h>
void longjmp(jump_buf env, int retval);
void siglongjump(sigjmp_buf env, int retval);
8.7 tools for manipulating process
ps top pmap /proc