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 CPSstatic 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 1static 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; }
