Linux环境编程第五天笔记
kill和kill()
kill 是一个发送信号的系统调用,也是是终止进程的命令。
返回值:成功:返回0;失败:返回-1;
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
| pid 值 | 含义 |
|---|---|
| > 0 | 发送给指定PID的进程 |
| = 0 | 发送给与调用进程同进程组的所有进程 |
| = -1 | 发送给有权限发送的所有进程(除了init进程) |
| < -1 | 发送给进程组ID等于 |pid| 的所有进程 |
sig 参数:信号编号
| 常用信号 | 编号 | 默认动作 | 说明 |
|---|---|---|---|
| SIGTERM | 15 | 终止 | 优雅终止(可捕获处理) |
| SIGKILL | 9 | 终止 | 强制终止(不可捕获、不可忽略) |
| SIGINT | 2 | 终止 | 终端中断(Ctrl+C) |
| SIGSTOP | 19 | 停止 | 暂停进程(不可捕获) |
| SIGCONT | 18 | 继续 | 继续被暂停的进程 |
| SIGUSR1 | 10 | 终止 | 用户自定义信号1 |
| SIGUSR2 | 12 | 终止 | 用户自定义信号2 |
| SIGHUP | 1 | 终止 | 终端挂断 |
kill命令常用用法
命令格式 功能说明
kill -l 查看系统所有的信号
kill -s SIGINT 3115 给PID为3115的进程发送SIGINT信号(使用的是信号名)
kill -2 3124 给PID为3124的进程发送2号信号(使用的是信号的值)
killall -s SIGINT a.out 给所有名为a.out的进程发送SIGINT信号
killall -2 a.out 给所有名为a.out的进程发送2号信号
signal()
接收一个信号
返回值:成功:返回最近一次调用时参数二的值;失败:返回SIG_ERR
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
-
signum:要接收的信号
-
handler:
-
SIG_IGN:忽略该信号。
-
SIG_DEF:执行缺省动作
-
void(*p):执行p指向的信号响应函数
-
raise()
给自己发送一个信号
返回值:成功:返回0;失败:返回非0
#include <signal.h>
int raise(int sig);
pause()
将本进程挂起,直到收到一个信号
返回值:收到非致命信号返回-1;收到致命信号不返回
#include <unistd.h>
int pause(void);
信号集操作函数
返回值:成功:sigismember返回1,其余返回0;失败:sigismember返回0,其余返回-1;
#include <signal.h>
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(sigset_t *set,int signum); //判断一个指定的信号是否被信号集包含
-
set :信号集
-
signum :要操作的信号
SIGKILL和SIGSTOP是两个特殊的信号,不可被忽略,不可被阻塞,不可被捕捉
sigprocmask()
阻塞或解除阻塞一个或多个信号
返回值:成功:返回0;失败:返回-1;
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
-
how :
-
SIG_BLOCK :将set中信号添加到当前阻塞信号集中
-
SIG_UNBLOCK :从当前信号集中移除set中的信号
-
SIG_SETMASK :把原有的阻塞信号集替换为当前的信号集
-
-
set :指向信号集的指针,如果为NULL,则how参数会被忽略
-
oldset :原有信号集,如果对原有信号集不感兴趣,可以设置为NULL
sigqueue()
向某进程发送一个指定数据,类似于kill,区别是sigqueue可以处理携带额外数据的信号。
返回值:成功:返回0;失败:返回-1
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
-
pid :目标进程的PID
-
sig :要发送的信号
-
value :携带的额外数据
union sigval {
int sival_int; // 传递整数
void *sival_ptr; // 传递指针(注意:跨进程传递指针通常无效,因为地址空间独立)
};
sigaction()
捕捉一个指定信号,且可以通过扩展函数获得信号携带的额外数据
返回值:成功:返回0;失败:返回-1;
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
-
sig :要捕捉的信号
-
act :NULL 时:仅查询当前处理方式;非 NULL 时:设置该信号的新处理方式;
struct sigaction
{
void (*sa_handler)(int); // 普通处理函数(无附加数据)
void (*sa_sigaction)(int, siginfo_t *, void *); //高级处理函数(可接收信号附带的数据)
sigset_t sa_mask; // 信号掩码:处理该信号时,临时阻塞这些信号
int sa_flags; // 标志位:控制信号处理的行为(核心)
void (*sa_restorer)(void); // 已废弃
}
高级处理函数参数
//siginfo_t 结构体
typedef struct siginfo {
int si_signo; // 1. 收到的信号编号(如 SIGUSR1=10)
int si_errno; // 2. 错误码(一般为 0,仅特殊信号有值)
int si_code; // 3. 信号产生的原因(如发送者是进程/内核)
pid_t si_pid; // 4. 发送信号的进程 PID(跨进程通信常用)
uid_t si_uid; // 5. 发送信号的进程所属用户 ID
union sigval si_value; // 6. 核心:信号附带的附加数据(就是你说的“数据+指针”联合体)
// 其他额外字段(针对特定信号,如 SIGSEGV 会有内存错误地址等,日常用得少)
} siginfo_t;
举例:
#include <stdio.h>
#include <signal.h>
void sig_handler(int signo, siginfo_t *info, void *context) {
printf("收到信号:%d\n", signo);
printf("发送信号的进程ID:%d\n", info->si_pid);
}
int main() {
struct sigaction act;
// 初始化清空结构体
sigemptyset(&act.sa_mask);
// 启用扩展信号处理模式
act.sa_flags = SA_SIGINFO;
// 指定扩展处理函数
act.sa_sigaction = sig_handler;
// 为SIGUSR1信号设置处理方式
sigaction(SIGUSR1, &act, NULL);
// 等待信号
while(1);
return 0;
}
act.sa_sigaction只绑定函数指针,而函数指针的参数需要使用sigqueue()进行单独传递
| sa_flags可选参数 | |
|---|---|
| SA_SIGINFO | 使用扩展信号响应函数 |
| SA_RESTART | 自动重启被信号中断的某些系统调用 |
| SA_NOCLDSTOP | 对于SIGCHLD信号,子进程暂停/恢复不提醒(不发送SIGCHLD) |
| SA_NOCLDWAIT | 对于SIGCHLD信号,子进程终止后,系统自动回收僵尸进程 |
| SA_NODEFER | 在处理当前信号时,不自动阻塞该信号本身(当信号处理函数执行时,内核会阻塞信号本身) |
| SA_ONSTACK | 使用预先设置的替代信号栈执行执行信号处理函数,不使用进栈主栈 |
| SA_RESETHAND | 信号处理函数执行一次之后,自动恢复该信号的默认处理方式 |
- oldact :NULL 时:不保存;非 NULL 时:保存该信号原来的处理方式,方便后续恢复;
atoi
atoi 是 C 标准库<stdlib.h>中的一个函数,作用是把字符串形式的数字转换成整数,成功返回转换后的数字,失败返回0。