我是靠谱客的博主 火星上蜗牛,最近开发中收集的这篇文章主要介绍linux 信号相关的知识,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

信号

信号的共性:

  1. 简单
  2. 不能携带大量的信息
  3. 要满足条件了才可以发送

信号的特质:

  1. 信号是软件层面的“中断”

    一旦信号产生,无论程序执行到了什么位置,必须立刻停止运行,来处理信号,之后再继续执行指令

  2. 所有的信号的产生和处理均是由内核产生和处理的

信号相关的事件和状态

信号的产生:

  1. 按键产生 Ctrl + c
  2. 系统调用产生 kill, raise
  3. 软件条件产生 alarm
  4. 硬件异常产生 段错误
  5. 命令产生 kill

递达: 传递且到达进程

未决: 产生和递达直接的状态, 由于阻塞所导致

信号的处理的方式:

  1. 执行默认动作
  2. 忽略
  3. 捕捉(调用用户处理函数)

阻塞信号集:

  • 将集合中的信号进行屏蔽, 若收到了该信号, 则会将其处理推后

未决信号集:

  • 信号产生时, 该信号集中的信号设置为1, 当信号处理后,设置为0
  • 若该信号被屏蔽, 则为解除前, 该信号一直为未决状态

常见信号:

9). SIGKILL 无条件终止进程,无法被忽略,处理和阻塞

19). SIGSTOP 停止进程, 无法被忽略,处理和阻塞

10). SIGUSR1 用户自定义信号, 默认为终止进程

12). SIGUSR2 用户自定义信号, 默认为终止进程

17). SIGCHLD 子进程的状态发生变化的时候,用于通知父进程, 默认为忽略

kill函数

int kill(pid_t pid, int sig);
参数:
pid
>0 : 把信号发送给指定的进程
=0 : 把信号发送给与调用kill函数相同进程组的所有的进程
<0 : 取|pid|发给对应的进程组(进程组的id和父进程的id是一样的)
== -1 : 发送给进程有权限发送的系统中的所有的进程
返回值:
成功: 0
失败: -1, 设置errno

raise函数

int raise(int sig);

等价于

kill(getpid(), sig);

abort函数

void abort(void);

用于终止进程

alarm函数

作用: 定时发送SIGALRM信号给进程(自然计时法 --- 无论进程处在什么状态, 始终计时)
unsigned int alarm(unsigned int seconds);
参数:
seconds: 定时的时间
返回值:
上次剩余的时间,
无错误现象

time命令: 查看程序执行的时间

实际时间 = 用户时间 + 内核时间 + 等待时间 -> 优化瓶颈在IO

setitimer/getitimer函数

int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
which: 指定定时方式
1. 自然定时: ITIMER_REAL	-> SIGALRM
2. 虚拟空间计时(用户空间): ITIMER_VIRTUAL	-> SIGVTALRM
3. 运行时计时(用户 + 内核): ITIMERR_PROF	-> SIGPROF
new_value: 定时秒数
old_value: 传出参数, 上次定时剩余时间
返回值:
成功: 0
失败: -1, 设置errno
struct itimerval {
struct timeval it_interval; 周期定时的秒数
struct timeval it_value;
第一次计时的秒数
};
struct timeval {
time_t
tv_sec;
/* seconds */
suseconds_t tv_usec;
/* microseconds */
};

信号集操作函数

总共有两个信号集, 一个是阻塞信号集, 一个是未决信号集, 可以操作阻塞信号集从而来间接影响未决信号集

sigset_t set: 自定义信号集

int sigemptyset(sigset_t *set);
清空信号集
int sigfillset(sigset_t *set);
全部阻塞
int sigaddset(sigset_t *set, int signum);	在一个信号集中阻塞一个信号
int sigdelset(sigset_t *set, int signum);	在一个信号集中不阻塞一个信号
int sigismember(const sigset_t *set, int signum);
判断一个集合是否被阻塞
返回值:
被阻塞了: 1
没有被阻塞: 0
失败: -1

设置信号屏蔽字:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:
how:
SIG_BLOCK:
设置阻塞
SIG_UNBLOCK:
取消阻塞
SIG_SETMASK:	用自定义的set替换mask
set:自定义的set
lodset:旧的mask, 传出参数
返回值:
成功: 0
失败: -1

查看未决信号集

int sigpending(sigset_t *set);
参数:
set: 传出参数, 未决信号集

信号的捕捉

signal函数

函数指针

函数返回值类型 (* 指针变量名) (函数参数列表);

函数指针类型

typedef 函数返回值类型 (* 指针变量类型名) (函数参数列表);

作用: 用于 注册 一个信号捕捉函数(执行依然是内核)
sighandler_t signal(int signum, sighandler_t handler);
参数:
signum: 信号的数字
handler: 一个函数指针, 指向的是一个形参为int, 返回值为void的函数
返回值:
handler
typedef void (*sighandler_t)(int);

sigaction函数

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
signum: 信号的编号
act: 新的动作
oldact: 旧的动作
struct sigaction {
void
(*sa_handler)(int);// 设置回调函数
1. 捕捉函数名
2. SIG_IGN表示忽略
3. SIG_DFL表示执行默认动作
void
(*sa_sigaction)(int, siginfo_t *, void *);
// 传递的参数, 要传参数的时候
sigset_t
sa_mask;
// 设置sa_handler函数工作的时候mask的值
int
sa_flags;
// 设置属性值
0: 默认动作
SA_RESTART: 被中断的时候重启
SA_SIGINFO: 使用sa_sigaction来指定捕捉函数
SA_DEFER: 不自动屏蔽信号
SA_INTERRUPT: 系统调用被信号中断后,不重启
void
(*sa_restorer)(void);
};

信号捕捉的特性

  1. 当一个捕捉函数捕捉到了一个信号以后, 当调用该函数的时候, 信号屏蔽字由sa_mask决定, 不由原来的信号屏蔽字决定, 调用完了以后, 则恢复成原来的信号屏蔽字
  2. XXX 信号捕捉函数期间, XXX信号会自动被屏蔽
  3. 阻塞的常规信号不支持排队, 产生多次, 只执行一次

sigchld 信号

sigchld的产生的条件

  1. 子进程终止时
  2. 子进程接收到SIGSTOP信号停止时
  3. 子进程处在停止态, 接受到SIGCONT后唤醒时

编程技巧

如何防止子进程在捕捉函数注册前就已经死掉?

  • 在捕捉函数注册完成以前,将信号屏蔽起来

如何防止僵尸进程的出现?

  • 使用循环来回收子进程, 一次捕捉, 多次回收 while ((wpid = waitpid(-1, &status, 0)) != -1);

慢速系统调用

  • 可能造成进程永久堵塞的系统调用叫慢速系统调用, 如read, wait, waitpid

被中断的行为

  1. 中断系统调用的信号不能被屏蔽, 忽略, 必须被捕捉
  2. 中断后返回-1, 设置errnoEINTR
  3. sa_flags
    1. SA_RESTART 重启
    2. SA_INTERRUPT 不重启

最后

以上就是火星上蜗牛为你收集整理的linux 信号相关的知识的全部内容,希望文章能够帮你解决linux 信号相关的知识所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部