在操作系统中,信号是一种进程间通信的机制,用于通知进程发生了某个特定事件。进程可以识别并处理不同的信号,以执行相应的操作。以下是关于信号的一些重要概念和相关代码示例:
进程对信号的处理能力
1.信号的处理能力是进程内置的一部分。 进程必须识别并能够处理信号。即使信号没有直接产生,进程也要具备处理信号的能力。
2.进程即便是没有收到信号,也能知道哪些信号该怎么处理。 进程可以预先定义对不同信号的处理方式,即使在信号产生之前,进程也已经知道如何处理这些信号。
3.进程可能并不会立即处理收到的信号。 当进程真的收到了一个具体的信号时,它可能不会立即处理这个信号,而是在合适的时机进行处理。
4.一个进程必须当信号产生,到信号被处理,就一定会有时间窗口,进程具有临时保存哪些信号已经发生了的能力。
前台进程:执行./的程序(linux每次登录只允许一个前台进程运行)谁获取键盘输入,谁是前台进程
后台进程:在./xxx运行程序的时候 加上& 可以在执行程序时继续使用指令,变成了后台进程,你输入的指令都是给bash的,所有ctrl+c没办法停止
kill一共又62个信号,1-31为普通信号,34-64为实时信号;
SIGHUP (1): 终端挂起或控制进程终止。
SIGINT (2): 中断信号,通常由 Ctrl+C 产生。
SIGQUIT (3): 退出信号,通常由 Ctrl+\ 产生。
SIGILL (4): 非法指令。
SIGTRAP (5): 跟踪/断点陷阱。
SIGABRT (6): 异常终止。
SIGBUS (7): 总线错误。
SIGFPE (8): 浮点异常。
SIGKILL (9): 强制终止。
SIGUSR1 (10): 用户自定义信号1。
SIGSEGV (11): 段错误。
SIGUSR2 (12): 用户自定义信号2。
SIGPIPE (13): 向无读进程的管道写入。
SIGALRM (14): 定时器到期。
SIGTERM (15): 终止信号。
SIGSTKFLT (16): 协处理器栈错误。
SIGCHLD (17): 子进程终止或停止。
SIGCONT (18): 继续执行已停止的进程。
SIGSTOP (19): 停止进程。
SIGTSTP (20): 交互停止信号。
SIGTTIN (21): 后台进程尝试读取控制终端。
SIGTTOU (22): 后台进程尝试写入控制终端。
SIGURG (23): 紧急条件发生。
SIGXCPU (24): 超时软限制。
SIGXFSZ (25): 超出文件大小限制。
SIGVTALRM (26): 虚拟定时器到期。
SIGPROF (27): 计时器到期。
SIGWINCH (28): 窗口尺寸调整。
SIGIO (29): 异步 I/O 事件。
SIGPWR (30): 电源失效。
SIGSYS (31): 非法系统调用。
信号的相关代码示例
信号的基本操作
SIGNAL(2): 获取当前handler表
#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:信号编号
handler:自定义的动作
(只需要设置一次,往后都有效)
signal(2,SIG_IGN);忽略2号信号
signal(2,SIG_DFL);终止信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
// 定义信号处理函数
void sig_handler(int signo) {
if (signo == SIGINT) {
printf("Received SIGINT signal\n");
exit(0);
}
}
int main() {
// 注册 SIGINT 信号处理函数
signal(SIGINT, sig_handler);
//signal(2,sig_handler);
while (1) {
// 进程执行其他任务
sleep(1);
}
return 0;
}
上述代码演示了如何注册一个信号处理函数,以处理接收到的 SIGINT 信号(通常由 Ctrl+C 产生)。
进程控制相关操作
KILL(2):
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int sig);
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int main() {
pid_t child_pid;
// 创建子进程
child_pid = fork();
if (child_pid == 0) {
// 在子进程中执行一些操作
printf("Child process executing\n");
sleep(2);
} else if (child_pid > 0) {
// 在父进程中等待一段时间
sleep(5);
// 向子进程发送 SIGTERM(15) 信号
kill(child_pid, SIGTERM);
}
return 0;
}
上述代码创建了一个父进程和一个子进程,父进程在等待一段时间后向子进程发送 SIGTERM 信号。这展示了父进程如何控制子进程的行为。
信号集的操作
sigset_t类型是位图类型 必须配合提供的接口使用
#include <signal.h>
int sigemptyset(sigset_t* set); 清空信号集 全部置零
int sigfillset(sigset_t* set); 全部置1
int sigaddset(sigset_t* set, int signo);添加一个特定的信号
int sigdelset(sigset_t* set, int signo);在特定的信号集中去除
int sigismember(const sigset_t* set, int signo);判断一个信号是否在信号集中
sigprocmask:可读取或更改进程的信号屏蔽字(阻塞信号集) (信号屏蔽字:block信号集)
#include <signal.h>
int sigprocmask(int how,const sigset_t* set, sigset_t * oset);成功为0,失败返回-1
how:此参数指定如何更改信号掩码。它可以取以下值之一:
SIG_BLOCK:将集合中的信号添加到当前信号掩码。 mask = mask | set
SIG_UNBLOCK:从当前信号掩码中移除集合中的信号。 mask = mask & ~set
SIG_SETMASK:将信号掩码设置为 set 参数中指定的值。mask = set
set:指向新信号掩码的指针。此集合中指定的信号将根据 how 参数的值受到影响。
oset:如果此参数不是 NULL,则函数调用之前的当前信号掩码将存储在 oset 指向的位置。
#include <stdio.h>
#include <signal.h>
int main() {
// 创建并初始化信号集
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
// 将信号集添加到当前进程的信号屏蔽字(阻塞信号集)中
sigprocmask(SIG_BLOCK, &sigset, NULL);
// 在这里执行其他任务,期间阻塞了 SIGUSR1 信号
// 从信号屏蔽字中移除 SIGUSR1 信号
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
return 0;
}
上述代码展示了如何使用信号集进行信号的阻塞和解除阻塞操作,以控制哪些信号可以被当前进程处理。
信号处理和子进程管理
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
// SIGCHLD 信号处理函数
void sigchld_handler(int signo) {
if (signo == SIGCHLD) {
printf("Received SIGCHLD signal\n");
}
}
int main() {
// 注册 SIGCHLD 信号处理函数
signal(SIGCHLD, sigchld_handler);
//SIGCHLD:表示子进程状态改变。通常,当一个子进程终止或停止运行时,父进程会收到 SIGCHLD 信号,以通知父进程有关子进程状态的变化。
pid_t child_pid;
// 创建子进程
child_pid = fork();
//signal(17,SIG_IGN);将SIGCHLD信号,设置为SIG_IGN,这样fork出来得子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。
SIGPENDING(2):
#include <signal.h> 获取当前pending表
int sigpending(sigset_t* set);返回值为 0 表示成功,-1 表示失败
set(输出型参数):指向 sigset_t 类型的指针,用于存储当前未决信号的集合。