1. 程式人生 > >第8章 異常控制流 (exceptional control flow)

第8章 異常控制流 (exceptional control flow)

calling lds osi multitask ons rec block 調度 nvi

# 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

第8章 異常控制流 (exceptional control flow)