目录
1.5 信号相关函数(kill、raise、alarm、pause、abort)
2.5 openmp 线程标准(相对于 posix 线程标准)
一、信号
1.1 信号的概念
信号是软件层面的中断。信号的响应依赖于中断。信号分为标准信号和实时信号。
kill -l 可以查看系统中的信号:
core 文件是程序出错的现场,可以使用 gdb 对 core 文件进行调试。
1.2 signal()
signal(2) 可以为特定的信号 signum 注册一个新的处理函数 handler,并且返回之前的处理函数。当出现特定的信号 signum 就会调用 handler。假如 handler 为 SIGIGN,则信号会被忽视;若 handler 为 SIGDFL,则会执行默认的处理函数。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
该函数实际的样子:
例子:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum)
{
write(1, "1", 1);
}
int main()
{
// signal(SIGINT, SIGIGN);
signal(SIGINT, sig_handler);
for (int i = 0; i < 10; i++) {
write(1, "*", 1);
sleep(1);
}
exit(0);
}
ctrl + c 可以发出 SIGINT 信号,所以每次使用 ctrl + c 都会调用一次信号处理函数 sig_handler:
重点:信号会打断阻塞的系统调用!!
如果 ctrl + c 按的很快的话可以看到 sleep 系统调用会被打断。 比如 open 和 read 系统调用中的两个错误码:
所以在前面例子中系统调用失败可能是由于信号导致的假错误,这时候我们可以重新进行一次系统调用。
不能随意地在信号处理函数中往外跳。
1.3 可重入函数
信号的不可靠,比如说第一次调用还没结束,第二次调用就开始了(连续两个相同信号到来)。可以使用可重入函数解决,可重入函数在第一次调用还没结束时发生第二次调用不会出错。
所有的系统调用都是可重入的,部分库函数是可重入的。
memcpy() 的两个内存地址空间不能重叠,而 memmove() 可以。
1.4 信号的响应过程(重点)
信号从收到到响应有一个不可避免的延迟。在从 kernel 返回到 user 态的时候才会查看 mask 和 pending 位图的按位与,然后响应信号。
如何忽略掉一个信号的?(mask 清 0)
标准信号为什么要丢失(多次置 pending 为 1,只响应一次)。
在收到多个标准信号时,标准信号的响应没有严格的顺序。
在响应信号的时候,mask 置 0,防止重入。
1.5 信号相关函数(kill、raise、alarm、pause、abort)
1. kill(2) 系统调用可以发送任意信号给任意进程或进程组。
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid 有如下四种情况:
- 当 pid 为正数时,sig 信号被发送给 pid 指定的进程。
- 当 pid 为 0 时,sig 信号会被发送给调用进程的进程组内的所有进程。
- 当 pid 为 -1 时,sig 信号会被发送给当前进程有权限发送信号的每一个进程,除了 init 进程(1 号进程)。
- 当 pid 小于 -1 时,sig 信号会被发送给 pgid 为 -pid 的进程组内的所有进程。
sig 参数为 0 时,不发送任何信号,可以用于检测进程和进程组是否存在(错误码为 ESRCH)。
2. raise(3) 可以给当前进程或线程发送信号。
#include <signal.h>
int raise(int sig);
在单线程的程序中 raise() 等效于:
kill(getpid(), sig);
在多线程的程序中 raise() 等效于:
pthread_kill(pthread_self(), sig);
3. alarm(2) 系统调用可以定时发送一个 SIGALRM 信号(注意不要在一个程序中多次使用 alarm ,多次使用时,只有最后一个 alarm 生效)。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
当 seconds 为 0 时,所有等待的 alarm 都被取消。
alarm 可以用于实现流量控制,有如下两种方式:
- 漏桶,就算海量的数据到来,还是以固定的速率处理数据,但没有数据的时候会死等。
- 令牌桶, 没有数据的时候会攒令牌,当数据到来的时候可以根据令牌数量处理更多的数据。
例子,mytbf,可以使用令牌桶来读取文件内容:
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include "mytbf.h"
#define CPS 10
#define BUFSIZE 1024
#define BURST 100
int main(int argc, char **argv)
{
int sfd, dfd = 1;
char buf[BUFSIZE];
int len, ret, pos, token_nums;
mytbf_t *tbf;
if (argc < 2) {
fprintf(stderr, "Usage...\n");
exit(1);
}
tbf = mytbf_init(CPS, BURST);
if (tbf == NULL) {
fprintf(stderr, "tbf is NULL\n");
exit(1);
}
do {
if ((sfd = open(argv[1], O_RDONLY)) < 0) {
if (errno != EINTR) {
perror("open()");
exit(1);
}
}
} while (sfd < 0);
while (1) {
token_nums = mytbf_