Concept

  1. 概念
    1. 信号是软件中断
    2. 信号的响应依赖于中断
  2. signal()
    • void (*signal)(int) signal(int signum, void (*func)(int))
    • 信号会打断阻塞的系统调用
  3. . 信号的不可靠(信号的行为不可靠)
  4. 可重入函数
    • 所有的系统调用都是可重入的,一部分库函数也是可重入的,如:memcpy。不可重入的函数有可能有_r版本,来做到可重入
  5. 信号的响应过程
    内核会给每个进程维护两个向量表(mask和pending),每当进程从kernel态到user态,会对两个向量表进行与运算,对应位置上的mask&pending如果为1,那么进程将响应对应的信号。此时内核会将二者均置位0,当响应完毕后,回到内核,恢复对应位置的mask为1(这样的做法是为了防止重入现象),当下一次回到user态,系统将继续原来正常的流程
    1. 如何忽略掉一个信号?
      • 把对应信号的mask位置为0,即信号屏蔽操作
    2. 标准信号为什么要丢失?
      • 因为是位图,当程序再执行注册signal动作函数的过程中,再来了一万个信号,也是仅仅置1,下一次从kernel到user只会执行一次signal动作,故此时到来的其他9999个信号相当于会被丢弃
    3. 标准信号的缺陷:标准信号没有一个严格的顺序
    • 信号从收到到响应有一个不可避免的延迟
  6. 常用函数
    • kill (int pid, int sig)
    • raise (int sig) 给自己发送一个信号,如 进程的kill和线程的pthread_kill
    • alarm (unsigned int seconds)
      • 多个谁放到后面谁有作用,所以不能作为多任务计时器
    • setitimer ()
      • 例:使用单一计时器,构造一组函数,实现任意数量计时器
    • pause (void) 等待一个信号来打断
    • system
    • sleep

volatile不轻信内存中存储的数值,去真实的地址拿数值

  1. // 使用signal和alarm,完成五秒中内的计数
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <time.h>
  5. #include <signal.h>
  6. #include <sys/types.h>
  7. static int jug = 0;
  8. static void alrm_handler(int arg) {
  9. jug = 1;
  10. }
  11. int main() {
  12. int64_t count = 0;
  13. signal(SIGALRM, alrm_handler);
  14. alarm(5);
  15. while (jug != 1) {
  16. count++;
  17. }
  18. printf("%lld\n", count);
  19. return 0;
  20. }
  21. // 但是如果开启O2优化,即会优化上述while中jug的指令,导致死循环
  22. // 所以必须给jug使用volatile,让程序去真实地址取数据,减去这一块的优化
  1. 信号集
  2. 信号屏蔽字/pending集处理
  3. 扩展
    • sigsuspend
    • sigaction
    • setitimer
  4. 实时信号

Exercise

  1. 慢速cat
    • 使用volatile、判断read打开是否为真、signal和alarm延时
    • 漏桶模型
  1. #include <fcntl.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <errno.h>
  6. #include <sys/stat.h>
  7. #include <signal.h>
  8. #define CPS 10
  9. #define BUFSIZE CPS
  10. static volatile int loop = 0;
  11. void alrm_handler(int arg) {
  12. alarm(1);
  13. loop = 1;
  14. }
  15. int main(int argc, char **argv) {
  16. int sfd, dfd = 1;
  17. char buf[BUFSIZE];
  18. int len, ret, pos;
  19. if (2 > argc) {
  20. fprintf(stderr, "Too less arguments\n");
  21. exit(0);
  22. }
  23. signal(SIGALRM, alrm_handler);
  24. alarm(1);
  25. do {
  26. sfd = open(argv[1], O_RDONLY);
  27. if (!sfd) {
  28. if (errno != EINTR) {
  29. perror("open\n");
  30. exit(1);
  31. }
  32. }
  33. } while(sfd < 0);
  34. while (1) {
  35. while (!loop)
  36. pause();
  37. loop = 0;
  38. while ((len = read(sfd, buf, BUFSIZE)) < 0) {
  39. if (len < 0) {
  40. if (errno == EINTR) {
  41. continue;
  42. }
  43. perror("read\n");
  44. break;
  45. }
  46. }
  47. pos = 0;
  48. if (len == 0)
  49. break;
  50. while (len > 0) {
  51. ret = write(dfd, buf + pos, len);
  52. if (ret < 0) {
  53. if (errno == EINTR) {
  54. continue;
  55. }
  56. perror("write\n");
  57. exit(1);
  58. }
  59. pos += ret;
  60. len -= ret;
  61. }
  62. }
  63. close(sfd);
  64. return 0;
  65. }
  • 令牌桶模型
    • 随着时间令牌积累,我们能够单次读取的流量越大
    • 存在令牌上限
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <stdint.h>
  7. #include <unistd.h>
  8. #define MAX_TOKEN 100
  9. #define TOKEN_GAP 10
  10. #define BUFSIZE 20
  11. #define TIME_GAP 1
  12. static int token = 0;
  13. volatile static int loop = 1;
  14. static void tkAlarm(int arg) {
  15. token += TOKEN_GAP;
  16. if (token >= MAX_TOKEN) {
  17. token = MAX_TOKEN;
  18. }
  19. alarm(TIME_GAP);
  20. loop = 0;
  21. }
  22. int main(int argc, char **argv)
  23. {
  24. if (argc < 2) {
  25. printf("Given arguments are less than 2\n");
  26. goto err;
  27. }
  28. int sfd = 0, dfd = 0;
  29. int len = 0, pos = 0, ret = 0;
  30. uint8_t buf[MAX_TOKEN * BUFSIZE] = {0};
  31. do {
  32. sfd = open(argv[1], O_RDONLY);
  33. if (sfd <= 0) {
  34. if (errno != EINTR) {
  35. perror("open");
  36. exit(-1);
  37. }
  38. }
  39. } while(sfd <= 0);
  40. dup2(dfd, STDOUT_FILENO);
  41. signal(SIGALRM, tkAlarm);
  42. alarm(TIME_GAP);
  43. while (1) {
  44. while (loop)
  45. pause();
  46. loop = 1;
  47. while ((len = read(sfd, buf, token * BUFSIZE)) < 0) {
  48. if (len < 0) {
  49. if (errno != EINTR) {
  50. perror("read");
  51. exit(-1);
  52. }
  53. }
  54. }
  55. pos = 0;
  56. if (len == 0)
  57. break;
  58. while (len > 0) {
  59. ret = write(dfd, buf + pos, len);
  60. if (ret < 0) {
  61. if (errno == EINTR) {
  62. continue;
  63. }
  64. perror("write");
  65. exit(-1);
  66. }
  67. pos += ret;
  68. len -= ret;
  69. }
  70. }
  71. close(sfd);
  72. err:
  73. return 0;
  74. }
  • setitimer ```c

    include

    include

    include

    include

    include

    include

    include

    include

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; }

  1. int sfd = 0, dfd = 0;
  2. int len = 0, pos = 0, ret = 0;
  3. uint8_t buf[MAX_TOKEN * BUFSIZE] = {0};
  4. do {
  5. sfd = open(argv[1], O_RDONLY);
  6. if (sfd <= 0) {
  7. if (errno != EINTR) {
  8. perror("open");
  9. exit(-1);
  10. }
  11. }
  12. } while(sfd <= 0);
  13. dup2(dfd, STDOUT_FILENO);
  14. signal(SIGALRM, tkAlarm);
  15. itvInit();
  16. if (setitimer(ITIMER_REAL, &itv, NULL) < 0) {
  17. perror("setitimer");
  18. exit(-1);
  19. }
  20. while (1) {
  21. while (loop)
  22. pause();
  23. loop = 1;
  24. while ((len = read(sfd, buf, token * BUFSIZE)) < 0) {
  25. if (len < 0) {
  26. if (errno != EINTR) {
  27. perror("read");
  28. exit(-1);
  29. }
  30. }
  31. }
  32. pos = 0;
  33. if (len == 0)
  34. break;
  35. while (len > 0) {
  36. ret = write(dfd, buf + pos, len);
  37. if (ret < 0) {
  38. if (errno == EINTR) {
  39. continue;
  40. }
  41. perror("write");
  42. exit(-1);
  43. }
  44. pos += ret;
  45. len -= ret;
  46. }
  47. }
  48. close(sfd);

err: return 0; }

```

Projects

Ref

[1] C/C++学习路线图—Linux高并发服务器开发