Concept
- 概念
- 信号是软件中断
- 信号的响应依赖于中断
- signal()
void (*signal)(int) signal(int signum, void (*func)(int))
- 信号会打断阻塞的系统调用
- . 信号的不可靠(信号的行为不可靠)
- 可重入函数
- 所有的系统调用都是可重入的,一部分库函数也是可重入的,如:memcpy。不可重入的函数有可能有
_r
版本,来做到可重入
- 所有的系统调用都是可重入的,一部分库函数也是可重入的,如:memcpy。不可重入的函数有可能有
- 信号的响应过程
内核会给每个进程维护两个向量表(mask和pending),每当进程从kernel态到user态,会对两个向量表进行与运算,对应位置上的mask&pending
如果为1,那么进程将响应对应的信号。此时内核会将二者均置位0,当响应完毕后,回到内核,恢复对应位置的mask为1(这样的做法是为了防止重入现象),当下一次回到user态,系统将继续原来正常的流程- 如何忽略掉一个信号?
- 把对应信号的mask位置为0,即
信号屏蔽操作
- 把对应信号的mask位置为0,即
- 标准信号为什么要丢失?
- 因为是位图,当程序再执行注册signal动作函数的过程中,再来了一万个信号,也是仅仅置1,下一次从kernel到user只会执行一次signal动作,故此时到来的其他9999个信号相当于会被丢弃
- 标准信号的缺陷:标准信号没有一个严格的顺序
- 信号从收到到响应有一个不可避免的延迟
- 如何忽略掉一个信号?
- 常用函数
kill (int pid, int sig)
raise (int sig)
给自己发送一个信号,如 进程的kill和线程的pthread_killalarm (unsigned int seconds)
- 多个谁放到后面谁有作用,所以不能作为多任务计时器
setitimer ()
- 例:使用单一计时器,构造一组函数,实现任意数量计时器
pause (void)
等待一个信号来打断- system
- sleep
volatile
不轻信内存中存储的数值,去真实的地址拿数值
// 使用signal和alarm,完成五秒中内的计数
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
static int jug = 0;
static void alrm_handler(int arg) {
jug = 1;
}
int main() {
int64_t count = 0;
signal(SIGALRM, alrm_handler);
alarm(5);
while (jug != 1) {
count++;
}
printf("%lld\n", count);
return 0;
}
// 但是如果开启O2优化,即会优化上述while中jug的指令,导致死循环
// 所以必须给jug使用volatile,让程序去真实地址取数据,减去这一块的优化
- 信号集
- 信号屏蔽字/pending集处理
- 扩展
- sigsuspend
- sigaction
- setitimer
- 实时信号
Exercise
- 慢速cat
- 使用volatile、判断read打开是否为真、signal和alarm延时
- 漏桶模型
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <signal.h>
#define CPS 10
#define BUFSIZE CPS
static volatile int loop = 0;
void alrm_handler(int arg) {
alarm(1);
loop = 1;
}
int main(int argc, char **argv) {
int sfd, dfd = 1;
char buf[BUFSIZE];
int len, ret, pos;
if (2 > argc) {
fprintf(stderr, "Too less arguments\n");
exit(0);
}
signal(SIGALRM, alrm_handler);
alarm(1);
do {
sfd = open(argv[1], O_RDONLY);
if (!sfd) {
if (errno != EINTR) {
perror("open\n");
exit(1);
}
}
} while(sfd < 0);
while (1) {
while (!loop)
pause();
loop = 0;
while ((len = read(sfd, buf, BUFSIZE)) < 0) {
if (len < 0) {
if (errno == EINTR) {
continue;
}
perror("read\n");
break;
}
}
pos = 0;
if (len == 0)
break;
while (len > 0) {
ret = write(dfd, buf + pos, len);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
perror("write\n");
exit(1);
}
pos += ret;
len -= ret;
}
}
close(sfd);
return 0;
}
- 令牌桶模型
- 随着时间令牌积累,我们能够单次读取的流量越大
- 存在令牌上限
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#define MAX_TOKEN 100
#define TOKEN_GAP 10
#define BUFSIZE 20
#define TIME_GAP 1
static int token = 0;
volatile static int loop = 1;
static void tkAlarm(int arg) {
token += TOKEN_GAP;
if (token >= MAX_TOKEN) {
token = MAX_TOKEN;
}
alarm(TIME_GAP);
loop = 0;
}
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Given arguments are less than 2\n");
goto err;
}
int sfd = 0, dfd = 0;
int len = 0, pos = 0, ret = 0;
uint8_t buf[MAX_TOKEN * BUFSIZE] = {0};
do {
sfd = open(argv[1], O_RDONLY);
if (sfd <= 0) {
if (errno != EINTR) {
perror("open");
exit(-1);
}
}
} while(sfd <= 0);
dup2(dfd, STDOUT_FILENO);
signal(SIGALRM, tkAlarm);
alarm(TIME_GAP);
while (1) {
while (loop)
pause();
loop = 1;
while ((len = read(sfd, buf, token * BUFSIZE)) < 0) {
if (len < 0) {
if (errno != EINTR) {
perror("read");
exit(-1);
}
}
}
pos = 0;
if (len == 0)
break;
while (len > 0) {
ret = write(dfd, buf + pos, len);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
perror("write");
exit(-1);
}
pos += ret;
len -= ret;
}
}
close(sfd);
err:
return 0;
}
define MAX_TOKEN 100
define TOKEN_GAP 10
define BUFSIZE 20
define TIME_GAP 1
static int token = 0; volatile static int loop = 1; static struct itimerval itv;
static void tkAlarm(int arg) { token += TOKEN_GAP; if (token >= MAX_TOKEN) { token = MAX_TOKEN; } loop = 0; }
void itvInit() { itv.it_interval.tv_sec = 1; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 1; itv.it_value.tv_usec = 0; }
int main(int argc, char **argv) { if (argc < 2) { printf(“Given arguments are less than 2\n”); goto err; }
int sfd = 0, dfd = 0;
int len = 0, pos = 0, ret = 0;
uint8_t buf[MAX_TOKEN * BUFSIZE] = {0};
do {
sfd = open(argv[1], O_RDONLY);
if (sfd <= 0) {
if (errno != EINTR) {
perror("open");
exit(-1);
}
}
} while(sfd <= 0);
dup2(dfd, STDOUT_FILENO);
signal(SIGALRM, tkAlarm);
itvInit();
if (setitimer(ITIMER_REAL, &itv, NULL) < 0) {
perror("setitimer");
exit(-1);
}
while (1) {
while (loop)
pause();
loop = 1;
while ((len = read(sfd, buf, token * BUFSIZE)) < 0) {
if (len < 0) {
if (errno != EINTR) {
perror("read");
exit(-1);
}
}
}
pos = 0;
if (len == 0)
break;
while (len > 0) {
ret = write(dfd, buf + pos, len);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
perror("write");
exit(-1);
}
pos += ret;
len -= ret;
}
}
close(sfd);
err: return 0; }