还没有笔记
选中页面文字后点击「高亮」按钮添加
COMS W3157
Dr. Borowski
考虑以下代码片段:
```
int main() {
while (1) {
sleep(5);
printf("Hello\n");
}
}
```
我们如何返回命令提示符?
信号是对一个事件的通知,该事件会引起操作系统的注意。信号可能来自:

| Number | Name | 默认操作 | 对应事件 |
| :--- | :--- | :--- | :--- |
| 1 | SIGHUP | 终止 | 终端线路挂断 |
| 2 | SIGINT | 终止 | 键盘中断 |
| 3 | SIGQUIT | 终止 | 键盘退出 |
| 4 | SIGILL | 终止 | 非法指令 |
| 5 | SIGTRAP | 终止并生成核心转储 ${ }^{\text {a }}$ | 跟踪陷阱 |
| 6 | SIGABRT | 终止并生成核心转储 ${ }^{\text {a }}$ | 来自 abort 函数的终止信号 |
| 7 | SIGBUS | 终止 | 总线错误 |
| 8 | SIGFPE | 终止并生成核心转储 ${ }^{\text {a }}$ | 浮点异常 |
| 9 | SIGKILL | 终止 ${ }^{\text {b }}$ | 杀死程序 |
| 10 | SIGUSR1 | 终止 | 用户定义信号 1 |
| 11 | SIGSEGV | 终止并生成核心转储 ${ }^{\text {a }}$ | 无效内存引用(段错误) |
| 12 | SIGUSR2 | 终止 | 用户定义信号 2 |
| 13 | SIGPIPE | 终止 | 写入无读取器的管道 |
| 14 | SIGALRM | 终止 | 来自 alarm 函数的定时器信号 |
| 15 | SIGTERM | 终止 | 软件终止信号 |
| 16 | SIGSTKFLT | 终止 | 协处理器栈错误 |
| 17 | SIGCHLD | 忽略 | 子进程已停止或终止 |
| 18 | SIGCONT | 忽略 | 如果停止则继续进程 |
| 19 | SIGSTOP | 停止直到下一个 SIGCONT ${ }^{\text {b }}$ | 非终端停止信号 |
| 20 | SIGTSTP | 停止直到下一个 SIGCONT | 来自终端的停止信号 |
| 21 | SIGTTIN | 停止直到下一个 SIGCONT | 后台进程从终端读取 |
| 22 | SIGTTOU | 停止直到下一个 SIGCONT | 后台进程写入终端 |
| 23 | SIGURG | 忽略 | 套接字上的紧急条件 |
| 24 | SIGXCPU | 终止 | CPU 时间限制超出 |
| 25 | SIGXFSZ | 终止 | 文件大小限制超出 |
| 26 | SIGVTALRM | 终止 | 虚拟定时器过期 |
| 27 | SIGPROF | 终止 | 分析定时器过期 |
| 28 | SIGWINCH | 忽略 | 窗口大小改变 |
| 29 | SIGIO | 终止 | 描述符上现在可以进行 I/O |
| 30 | SIGPWR | 终止 | 电源故障 |
Linux 信号。注:(a) 多年前,主内存采用一种称为磁芯内存(core memory)的技术实现。“生成核心转储”(Dumping core)是一个历史术语,指将代码和数据内存段的映像写入磁盘。(b) 该信号既不能被捕获也不能被忽略。(来源:man 7 signal。数据来自 Linux 基金会。)
unsigned int alarm(unsigned int seconds) 设置一个警报,并在其过期时发送一个名为 SIGALRM 的信号。
int kill(pid_t pid, int sig) 将指定的信号发送到指定的进程。请注意,每个信号都与一个正整数相关联,这就是为什么 sig 作为 int 传递的原因。
raise (int sig) 是 kill() 的一个特例,它将指定的信号发送到当前运行的进程。
信号处理。接收到信号会触发控制转移到信号处理程序。处理完成后,处理程序将控制权返回给被中断的程序。

typedef void (*sighandler_t) (int);
sighandler_t signal
(int signum, sighandler_t handler)
设置当遇到信号 signum 时要使用的处理程序为 handler,并返回相同的处理程序。请注意,handler 是一个函数指针。
```
#include
#include
#include
void sighandler(int signo) {
if (signo == SIGINT) {
printf("Received SIGINT\n");
}
}
int main() {
if (signal(SIGINT, sighandler) == SIG_ERR) {
printf("Can't catch SIGINT\n");
}
// A long sleep so we can issue a signal.
while (1) {
sleep (1);
}
return 0;
}
```
int sigaction(int signum, const struct sigaction act, struct sigaction oldact)
```
struct sigaction {
void (*sa_handler) (int);
void (sa_sigaction)(int, siginfo_t , void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
```
sa_handler 是接收到信号时调用的函数,而 sa_sigaction 是一个 sigaction 调用。它们是互斥的,不应同时设置。
```
struct sigaction {
void (*sa_handler) (int);
void (sa_sigaction)(int, siginfo_t , void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
```
```
struct sigaction {
void (*sa_handler) (int);
void (sa_sigaction)(int, siginfo_t , void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
```
siginfo_t 数据类型是一个包含以下字段的结构体:
```
siginfo_t {
int- si_signo; / Signal number /
int si-errno; / An errno value /
int si_code; / Signal code /
int si_trapno; /* Trap number that caused
hardware-generated signal
/ (unused on most architectures) */
pid_t si_pid; / Sending process ID /
uid_t si_uid; / Real user ID of sending process /
int- si_status; / Exit value or signal /
clock t si-utime; / User time consumed /
clock_t si-stime; / System time consumed /
union-sigvaI si_value; / Signal value /
int si_int; / POSIX.1b signal /
void si_ptr; / POSIX.1b signal */
int si_overrun; /* Timer overrun count;
POSIX.1b timers */
int si timerid; / Timer ID; POSIX.1b timers /
void si-addr; / Memory location which caused fault */
long si_band; /* Band event (was int in
int si_fd; / File descriptor /
short si_addr_lsb; /* Least significant bit of address
void si_lower; / Lower bound when address violation
occurred (since Linux 3.19) */
void si_upper; / Upper bound when address violation
occurred (since Linux 3.19) */
int si_pkey; /* Protection key on PTE that caused
fault (since Linux 4.6) */
void si_call_addr; / Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
}
```
```
#include
#include
#include
void handle(int sig, siginfo_t siginfo, void context) {
printf("Sending PID: %ld, UID: %ld\n",
(long)siginfo->si_pid, (long) siginfo->si_uid);
}
int main() {
struct sigaction act = {0};
// Use the sa_sigaction field because the handle has two additional
// parameters.
act.sa_sigaction = handle;
// The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not
// sa_handler.
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGTERM, &act, NULL) < 0) {
perror("sigaction");
return 1;
}
while (1) { sleep(1); }
return 0;
}
```
许多系统调用,如 read()、write() 和 getppid() 都是可重入的。这意味着如果信号中断了调用,它可以在信号处理程序执行完成后安全地重新启动。
| abort | faccessat | linkat | select | socketpair |
| :--- | :--- | :--- | :--- | :--- |
| accept | fchmod | listen | sem_post | stat |
| access | fchmodat | lseek | send | symlink |
| aio_error | fchown | lstat | sendmsg | symlinkat |
| aio_return | fchownat | mkdir | sendto | tcdrain |
| aio_suspend | fcntl | mkdirat | setgid | tcflow |
| alarm | fdatasync | mkfifo | setpgid | tcflush |
| bind | fexecve | mkfifoat | setsid | tcgetattr |
| cfgetispeed | fork | mknod | setsockopt | tcgetpgrp |
| cfgetospeed | fstat | mknodat | setuid | tcsendbreak |
| cfsetispeed | fstatat | open | shutdown | tcsetattr |
| cfsetospeed | fsync | openat | sigaction | tcsetpgrp |
| chdir | ftruncate | pause | sigaddset | time |
| chmod | futimens | pipe | sigdelset | timer_getoverrun |
| chown | getegid | poll | sigemptyset | timer_gettime |
| clock_gettime | geteuid | posix_trace_event | sigfillset | timer_settime |
| close | getgid | pselect | sigismember | times |
| connect | getgroups | raise | signal | umask |
| creat | getpeername | read | sigpause | uname |
| dup | getpgrp | readlink | sigpending | unlink |
| dup2 | getpid | readlinkat | sigprocmask | unlinkat |
| execl | getppid | recv | sigqueue | utime |
| execle | getsockname | recvfrom | sigset | utimensat |
| execv | getsockopt | recvmsg | sigsuspend | utimes |
| execve | getuid | rename | sleep | wait |
| _Exit | kill | renameat | sockatmark | waitpid |
| _exit | link | rmdir | socket | write |
图 10.4 可从信号处理程序调用的可重入函数