鱼C论坛

 找回密码
 立即注册
查看: 1887|回复: 0

[学习笔记] 013-信号进阶

[复制链接]
发表于 2018-11-9 16:15:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 moc 于 2018-11-15 17:03 编辑

1、信号在linux中的传递过程
信号递达(Delivery): 执行信号的处理动作称为。
信号未决(Pending):信号从产生到递达之间的状态。
        进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
        注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号在linux中的传递过程:
1.png

说明1: PCB进程控制块中函数有信号屏蔽状态字(block)信号未决状态字(pending)还有是否忽略标志;
说明2: 信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决,0代表信号可以抵达了
说明3:  向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,若阻塞则信号未决状态字相应位制成1;若阻塞解除,信号未决状态字相应位制成0;表示信号可以抵达了。
说明4: block状态字、pending状态字 都是64bit;
说明5: block状态字用户可以读写,pending状态字用户只能读, 这是信号设计机制。
信号集操作函数(状态字表示):
头文件:  #include <signal.h>
原型功能
int  sigemptyset(sigset_t *set) 把信号集64bit全设为0
int  sigfillset(sigset_t *set)把信号集全设为1
int  sigaddset(sigset_t *set, int signo)根据signo,把信号集中的对应为置成1
int  sigdelset(sigset_t *set, int signo)根据signo,把信号集中的对应为置成0
int  sigismember(const sigset_t *set, int signo)判断signo是否在信号集中
int sigprocmask(int how, const sigset_t *set, sigset_t *oset)读取或更改进程的信号屏蔽状态字(block)
int sigpending(sigset_t *set)获取信号未决状态字(pending)信息

sigprocmask函数:
功能: 读取或更改进程的信号屏蔽字。
返回值: 若成功则为0,若出错则为-1
        如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
        如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。
        如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。
假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。
2.png
  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <signal.h>

  6. // 演示信号从产生到抵达的整个过程
  7. // 信号的阻塞和解除阻塞的综合实验
  8. #define ERR_EXIT(m) \
  9.         do \
  10.         { \
  11.                 perror(m); \
  12.                 exit(EXIT_FAILURE); \
  13.         } while(0);
  14.        
  15. // 信号处理函数
  16. void handlder(int sig)
  17. {
  18.         if (sig == SIGINT)
  19.         {
  20.                 printf("recv a sig = %d \n", sig);
  21.         }
  22.         else if (sig == SIGQUIT)
  23.         {
  24.                 sigset_t uset;   // 自建一个信号集
  25.                 sigemptyset(&uset);   // 清空信号集
  26.                 sigaddset(&uset, SIGINT);
  27.                 // Ctrl + \ 来解除 SIGINT 信号
  28.                 // 解除阻塞
  29.                 sigprocmask(SIG_UNBLOCK, &uset, NULL);  // 更改信号屏蔽字block
  30.         }
  31. }
  32. // 打印信号集
  33. void printsigset(sigset_t *set)
  34. {
  35.         int i;
  36.         for (i=1; i<NSIG; ++i)
  37.         {
  38.                 if (sigismember(set, i))  // 查看信号集中是否有i信号
  39.                 {
  40.                         putchar('1');
  41.                 }
  42.                 else
  43.                 {
  44.                         putchar('0');
  45.                 }
  46.         }
  47.         printf("\n");
  48. }
  49. // 连续按键盘 Ctrl + c ,虽然发送了多个SIGINT信号,但因为信号是不稳定的,只保留了一个
  50. // 不支持排队   PCB  ==> task_struct
  51. int main(int argc, char *argv[])
  52. {
  53.         sigset_t pset;  // 用来打印信号集   未决状态字
  54.         sigset_t bset;  // 用来设置阻塞信号集
  55.        
  56.         sigemptyset(&bset);
  57.         sigaddset(&bset, SIGINT);   // 把信号集中相应的位置1
  58.        
  59.         // 注册信号
  60.         if (signal(SIGINT, handlder) == SIG_ERR)
  61.         {
  62.                 ERR_EXIT("signal error");
  63.         }
  64.         if (signal(SIGQUIT, handlder) == SIG_ERR)
  65.         {
  66.                 ERR_EXIT("signal error");
  67.         }
  68.        
  69.         // 读取或更改进程信号的屏蔽字  这里来阻塞信号Ctrl + c信号
  70.         // Ctrl + c信号被设置从阻塞, 即使用户按下ctrl+c键,也不会抵达
  71.         sigprocmask(SIG_BLOCK, &bset, NULL);  // 或的方式设置阻塞位
  72.        
  73.         for (;;)
  74.         {
  75.                 // 获取未决 状态字信息
  76.                 sigpending(&pset);
  77.                
  78.                 // 打印信号未决  sigset_t 字
  79.                 printsigset(&pset);
  80.                 sleep(2);
  81.         }
  82.         return 0;
  83. }
复制代码

2、sigaction函数注册信号处理函数、sigqueue新的信号发送函数
可带值的信号传递。
1. sigaction函数
功能: sigaction函数用于改变进程接收到特定信号后的行为。
原型: int  sigaction(int signum,const struct sigaction *act,const struct sigaction *old);
参数:         1>  第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误);
                2> 第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;
                3> 第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
返回值:   函数成功返回0,失败返回-1.
2. sigaction结构体
  1. struct sigaction {
  2.     void (*sa_handler)(int);        // 此参数和signal()的参数handler相同,代表新的信号处理函数,不接受额外数据
  3.     void (*sa_sigaction)(int, siginfo_t *, void *);        // 信号处理程序 能接受额外数据,和sigqueue配合使用
  4.     sigset_t sa_mask;        // 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置,信号最终还会抵达
  5.     int sa_flags;        // //影响信号的行为,  可选SA_SIGINFO表示能接受数据
  6. }
复制代码

注意:  回调函数句柄sa_handler、sa_sigaction只能任选其一。
3. siginfo_t结构体
  1. siginfo_t {
  2.                   int      si_signo;  /* Signal number */
  3.                   int      si_errno;  /* An errno value */
  4.                   int      si_code;   /* Signal code */
  5.                   pid_t    si_pid;    /* Sending process ID */
  6.                   uid_t    si_uid;    /* Real user ID of sending process */
  7.                   int      si_status; /* Exit value or signal */
  8.                   clock_t  si_utime;  /* User time consumed */
  9.                   clock_t  si_stime;  /* System time consumed */
  10.                   sigval_t si_value;  /* Signal value */
  11.                   int      si_int;    /* POSIX.1b signal */
  12.                   void *   si_ptr;    /* POSIX.1b signal */
  13.                   void *   si_addr;   /* Memory location which caused fault */
  14.                   int      si_band;   /* Band event */
  15.                   int      si_fd;     /* File descriptor */
  16.               }
复制代码
sigqueue函数
功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
原型:  int sigqueue(pid_t pid, int sig, const union sigval value);
参数:        第1个参数是指定接收信号的进程id,
                第2个参数确定即将发送的信号,
                第3个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。
返回值:           成功返回0,失败返回-1
注意:        和kill函数相比Int kill(pid_t pid, int siq)多了参数;sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
sigval联合体
  1. typedef union sigval {
  2.                 int sival_int;
  3.                 void *sival_ptr;
  4. }sigval_t;
复制代码
  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <signal.h>

  6. // 演示发送和接受带数据的信号
  7. #define ERR_EXIT(m) \
  8.         do \
  9.         { \
  10.                 perror(m); \
  11.                 exit(EXIT_FAILURE); \
  12.         } while(0);
  13.        
  14. typedef struct Teacher
  15. {
  16.         char name[64];
  17.         int age;
  18. }Teacher;

  19. // 用新的回调函数接受数据....
  20. void myHandle_forsigaction(int signum, siginfo_t *s_t, void *p)
  21. {
  22.         int myint = 0;
  23.         printf("可传输数据的回调函数.\n");
  24.         printf("recv signum : %d \n", signum);
  25.         myint = s_t->si_value.sival_int;
  26.         printf("%d %d \n", myint, s_t->si_int);
  27. }

  28. int main(int argc, char *argv[])
  29. {
  30.         pid_t pid;
  31.         int ret = 0;
  32.        
  33.         struct sigaction act;
  34.         act.sa_sigaction = myHandle_forsigaction;
  35.         sigemptyset(&act.sa_mask);  // sa_mask集中的信号在处理sa_sigaction函数时会暂时阻塞
  36.        
  37.         // 父进程的回调函数, 准备接受额外的数据
  38.         act.sa_flags = SA_SIGINFO;
  39.        
  40.         if (sigaction(SIGINT, &act, NULL) < 0)
  41.         {
  42.                 ERR_EXIT("sigaction error");
  43.         }
  44.        
  45.         pid = fork();
  46.         if (pid == -1)
  47.         {
  48.                 perror("fork error");
  49.                 return 0;
  50.         }
  51.         if (pid == 0)
  52.         {
  53.                 union sigval mysigval;
  54.                 mysigval.sival_int = 222;
  55.                
  56.                 ret = sigqueue(getppid(), SIGINT, mysigval);
  57.                 if (ret != 0)
  58.                 {
  59.                         printf("sigqueue .....\n");
  60.                         exit(0);
  61.                 }
  62.                 else
  63.                 {
  64.                         printf("sigqueue ...sucecess\n");
  65.                         sleep(2);
  66.                 }
  67.         }
  68.         else if (pid > 0)
  69.         {
  70.                 for (;;)
  71.                 {
  72.                         pause();
  73.                 }
  74.         }
  75.         return 0;
  76. }
复制代码

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-1 10:34

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表