信号
概述
/usr/include/
- 信号是进程间通信(
IPC
)的一种简单机制 - 信号提供了一种向进程传递通知和信息的方式,以便进程可以相应地做出反应
信号类型
SIGINT
:当用户按下CTRL-C
时,通常发送这个信号来中断进程SIGTERM
:用于正常终止进程,是kill
命令的默认信号SIGKILL
:用于立即终止进程,不允许进程执行任何清理操作SIGSTOP
:用于暂停进程的执行SIGCONT
:用于继续被停止的进程SIGSEGV
:当进程访问无效内存时生- 等
发送信号
- 可以使用
kill
命令或kill
系统调用来向进程发送信号
1 |
kill 1234 |
kill
- 示例
1 2 |
kill -s SIGTERM 1234 # 发送SIGTERM信号到进程ID为1234的进程 kill -9 1234 # 发送SIGKILL信号到进程ID为1234的进程 |
- 系统上可用的所有信号
- 可以用
kill
发送的信号(系统为debian
)
- 可以用
处理信号
- 进程可以选择忽略某些信号、执行默认操作或指定特定的处理程序
- 通过使用
signal
或sigaction
系统调用,进程可以注册特定的处理程序
1 2 3 4 5 6 7 8 9 10 11 |
#include <signal.h> #include <stdio.h> void handler(int signum) { printf("Received signal %d\n", signum); } int main() { signal(SIGINT, handler); // ... } |
默认行为
- 每个信号都有一个与之关联的默认行为,这可以是忽略信号、终止进程、终止并转储核心文件或停止/继续进程
异步信号
- 由其他进程或系统触发
同步信号
- 由进程自身的操作触发,如除以零导致的
SIGFPE
信号阻塞和未决
- 进程可以阻塞某些信号,使它们暂时不被处理
- 这些信号将变为未决,直到进程解除阻塞
定义
debian
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __ASM_GENERIC_SIGNAL_H #define __ASM_GENERIC_SIGNAL_H #include <linux/types.h> #define _NSIG 64 #define _NSIG_BPW __BITS_PER_LONG #define _NSIG_WORDS (_NSIG / _NSIG_BPW) #define SIGHUP 1 #define SIGINT 2 #define SIGQUIT 3 #define SIGILL 4 #define SIGTRAP 5 #define SIGABRT 6 #define SIGIOT 6 #define SIGBUS 7 #define SIGFPE 8 #define SIGKILL 9 #define SIGUSR1 10 #define SIGSEGV 11 #define SIGUSR2 12 #define SIGPIPE 13 #define SIGALRM 14 #define SIGTERM 15 #define SIGSTKFLT 16 #define SIGCHLD 17 #define SIGCONT 18 #define SIGSTOP 19 #define SIGTSTP 20 #define SIGTTIN 21 #define SIGTTOU 22 #define SIGURG 23 #define SIGXCPU 24 #define SIGXFSZ 25 #define SIGVTALRM 26 #define SIGPROF 27 #define SIGWINCH 28 #define SIGIO 29 #define SIGPOLL SIGIO /* #define SIGLOST 29 */ #define SIGPWR 30 #define SIGSYS 31 #define SIGUNUSED 31 /* These should not be considered constants from userland. */ #define SIGRTMIN 32 #ifndef SIGRTMAX #define SIGRTMAX _NSIG #endif #if !defined MINSIGSTKSZ || !defined SIGSTKSZ #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 #endif #ifndef __ASSEMBLY__ typedef struct { unsigned long sig[_NSIG_WORDS]; } sigset_t; /* not actually used, but required for linux/syscalls.h */ typedef unsigned long old_sigset_t; #include <asm-generic/signal-defs.h> #ifdef SA_RESTORER #define __ARCH_HAS_SA_RESTORER #endif struct sigaction { __sighandler_t sa_handler; unsigned long sa_flags; #ifdef SA_RESTORER __sigrestore_t sa_restorer; #endif sigset_t sa_mask; /* mask last for extensibility */ }; typedef struct sigaltstack { void *ss_sp; int ss_flags; __kernel_size_t ss_size; } stack_t; #endif /* __ASSEMBLY__ */ #endif /* __ASM_GENERIC_SIGNAL_H */ |
signal
概述
- 这个函数是
Unix
和Linux
系统中用来处理信号的一种机制signal
函数允许你定义某个信号发生时应该采取的动作
原型
1 2 3 4 5 |
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); |
参数
- 需要处理的信号编号
- 信号处理函数
SIG_DFL
: 使用该信号的默认处理动作SIG_IGN
: 忽略该信号
返回值
- 成功时,
signal
返回先前的信号处理函数的地址 - 失败时,返回
SIG_ERR
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdio.h> #include <signal.h> #include <unistd.h> void handler(int signum) { printf("Caught SIGINT, exiting...\n"); exit(0); } int main() { signal(SIGINT, handler); while (1) { printf("Running...\n"); sleep(1); } return 0; } |
sigaction
概述
- 函数在Unix和Linux系统中用于控制信号的处理行为
- 它可以更精确地定义一个信号的处理方式,提供了比
signal
函数更多的控制
原型
1 |
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); |
参数
- 信号编号,指定要处理的信号
- 指向一个
sigaction
结构体的指针,定义信号处理的新行为- 如果此参数为NULL,则不改变信号的行为,但仍可以用来查询旧的信号行为
- 指向一个
sigaction
结构体的指针,用于保存信号处理的旧行为- 如果此参数为NULL,则不返回旧的行为
返回值
- 成功时返回
0
,失败时返回-1
,并设置errno
以指示错误原因
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <stdio.h> #include <signal.h> #include <unistd.h> void handler(int signo) { printf("Caught SIGINT\n"); } int main() { struct sigaction sa; sa.sa_handler = handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); return 1; } printf("Press Ctrl-C\n"); pause(); // Wait for signals return 0; } |
信号集
概述
- 信号集在
Unix
和Linux
系统中是用于表示信号的集合的数据结构 - 这些集合通常用于定义哪些信号应该被阻塞(即暂时忽略)或等待处理
- 信号集对于管理复杂的信号处理逻辑非常有用
- 被设置为1的信号,都是属于当前被阻塞的不能传递的这个进程的信号
- 信号集可以包括一个或多个信号,或者根据需要不包括任何信号
- 你可以使用一组函数来操作这些集合,例如添加信号、删除信号、检查信号是否存在等
主要函数
- 初始化信号集,使其不包括任何信号
1 |
int sigemptyset(sigset_t *set) |
- 初始化信号集,使其包括所有可捕获的信号
1 |
int sigfillset(sigset_t *set) |
- 将指定的信号添加到信号集中
1 |
int sigaddset(sigset_t *set, int signum) |
- 从信号集中删除指定的信号
1 |
int sigdelset(sigset_t *set, int signum) |
- 检查指定的信号是否在信号集中。如果在集合中,返回1,如果不在,返回0
1 |
int sigismember(const sigset_t *set, int signum) |
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <signal.h> #include <stdio.h> int main() { sigset_t set; // Initialize the set to empty if (sigemptyset(&set) < 0) { perror("sigemptyset"); return 1; } // Add SIGINT to the set if (sigaddset(&set, SIGINT) < 0) { perror("sigaddset"); return 1; } // Check if SIGINT is a member of the set if (sigismember(&set, SIGINT)) { printf("SIGINT is in the set\n"); } else { printf("SIGINT is not in the set\n"); } // Remove SIGINT from the set if (sigdelset(&set, SIGINT) < 0) { perror("sigdelset"); return 1; } return 0; } |
进程状态
运行态
R
- 进程正在运行或在运行队列中等待运行
中断睡眠态
S
- 这是可中断的睡眠状态,进程等待某个条件的满足或某些资源的可用性
- 如果满足条件,进程将被唤醒并转入运行状态
不可中断睡眠态
D
- 这是一种不可中断的睡眠状态。进程正在等待
I/O
操作完成,或等待某些特殊资源- 在此状态下,进程不响应任何信号和中断
僵尸状态
Z
- 进程已终止,但父进程还没有读取其退出状态
- 进程的大部分资源都已释放,但进程描述符仍存在,以便父进程读取子进程的退出状态
停止状态
T
- 进程已停止执行
- 这可能是因为它收到了
SIGSTOP
、SIGTSTP
、SIGTTIN
或SIGTTOU
信号,或者它正在被调试
- 这可能是因为它收到了
追踪停止状态
t
- 进程由于调试而被停止
死亡状态
X
- 进程正在退出,但还未成为僵尸进程
唤醒杀死状态
K
- 进程被唤醒,以便被
kill
空闲状态
I
- 适用于内核线程,线程没有运行并处于空闲状态
状态字符<
<
- 表示进程具有高优先级(或称为负的
nice
值) - 在
Linux
中,nice
值范围从-20
(最高优先级)到19
(最低优先级)- 此标志表明进程具有比默认优先级更高的优先级
状态字符+
+
- 表示进程处于前台进程组
- 在终端交互中,前台进程组与用户交互,而后台进程组则独立于用户进行操作
操作系统执行模式
用户态
user mode
- 在用户态运行的程序受到许多权限限制,不能直接访问硬件或执行某些特权指令
- 用户态程序只能访问分配给它们的内存空间,不能访问其他进程的内存或内核空间
- 要执行特权操作(如读写文件、管理进程等),用户态程序必须通过系统调用请求内核提供这些服务
- 用户态程序的错误通常不会影响整个系统,因为它们的权限受限
- 注意:
- 不要在用户态程序中尝试执行需要特权的操作,而是使用正确的系统调用
- 确保代码不会尝试访问超出其分配范围的内存,以防止段错误等问题
内核态
kernel mode
- 在内核态中,代码可以执行任何CPU指令并直接访问所有硬件
- 内核态代码可以访问整个物理内存,包括所有用户空间和内核空间
- 内核态负责处理从用户态发起的系统调用
- 硬件驱动和核心系统服务通常在内核态中运行
- 注意:
- 内核代码的错误可能导致整个系统崩溃或不稳定,因此需要格外小心
- 由于内核态代码有权访问所有资源,因此需要确保对资源的访问经过适当的同步和保护,以防止竞争条件等问题
- 不要不必要地将代码放在内核态执行。如果可以在用户态完成,那么应该在用户态中完成
可重入函数
- 可重入函数是在多线程环境下可以被多个线程安全地并发调用的函数
- 特点:
- 不使用全局或静态数据
- 不修改自身的代码
- 不调用不可重入的函数
不可重入函数
- 不可重入函数可能使用全局或静态数据,或者可能调用其他不可重入的函数
- 在多线程环境中同时调用不可重入函数可能会导致数据竞争和不确定的行为
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Deelx正则引擎使用12/24
- ♥ WTL 概述03/10
- ♥ 【Manjaro】 pacman命令详解03/21
- ♥ Shell 语法记述 第三篇09/05
- ♥ C++_运算符优先级&&相关性12/15
- ♥ Linux 高性能服务器编程:网络基础编程二11/28