用户态 kill 与 pthread_kill 的区别?
在 Linux 用户态中,pthread_kill
和 kill
都能发送系统信号,但它们的作用对象、使用场景和行为有显著区别,核心差异在于信号的接收者不同:
1. 作用对象不同
kill(pid_t pid, int sig)
用于向 进程 发送信号,pid
参数指定目标进程的 ID:pid > 0
:向 ID 为pid
的进程发送信号pid = 0
:向当前进程所在进程组的所有进程发送信号pid = -1
:向系统中所有有权限发送的进程发送信号(特殊权限下)pid < -1
:向进程组 ID 为|pid|
的所有进程发送信号
pthread_kill(pthread_t thread, int sig)
用于向 同一进程内的特定 线程 发送信号,thread
参数是目标线程的 ID(由pthread_create
返回)。
2. 信号的处理范围不同
kill
发送的信号:属于进程级信号,默认由进程内的 “主线程” 或 “任意线程” 处理(取决于信号处理函数的注册方式)。
例如,若进程中注册了SIGINT
的处理函数,用kill
向该进程发送SIGINT
,会触发进程内的该处理函数(由内核选择一个线程执行)。pthread_kill
发送的信号:属于线程级信号,专门针对指定线程,信号处理函数在目标线程的上下文中执行。
例如,向 ‘线程 A’ 发送SIGUSR1
,若该信号已注册处理函数,则处理函数在 ‘线程 A’ 中运行。
3. 特殊信号的行为差异
- 对于会导致进程终止的信号(如
SIGKILL
、SIGTERM
):- 无论用
kill
还是pthread_kill
发送,最终都会导致整个进程终止(因为线程是进程的一部分,进程终止会带动所有线程终止)。 - 区别在于:
kill
直接针对进程,pthread_kill
虽指定线程,但信号的 “终止进程” 语义会作用于整个进程。
- 无论用
- 对于可捕获的信号(如
SIGUSR1
、SIGALRM
):kill
发送的信号可能被进程内任意线程捕获(内核调度决定)。pthread_kill
发送的信号只会被目标线程捕获(如果该线程未阻塞此信号)。
4. 使用场景不同
kill
的典型场景:
进程间通信,例如:- 终端通过
kill
向后台进程发送SIGTERM
要求其退出 - 父进程向子进程发送信号控制其行为
- 终端通过
pthread_kill
的典型场景:
同一进程内的线程间通信,例如:- 主线程向阻塞的子线程发送信号,中断其阻塞状态(如从
sleep
或wait
中唤醒) - 通知特定线程处理某个事件
- 主线程向阻塞的子线程发送信号,中断其阻塞状态(如从
示例对比
#include #include #include #include #include #include // 信号处理函数void sig_handler(int sig) { printf(\"信号 %d 在线程 %lu 中被处理\\n\", sig, pthread_self());}// 子线程函数void *thread_son_func(void *arg) { printf(\"子线程 %lu 启动\\n\", pthread_self()); //与ps -T -p 得到的线程ID无关,这样打印也不太规范 while (1) { sleep(1); // 模拟阻塞 } return NULL;}int main() { signal(SIGUSR1, sig_handler); // 注册信号处理函数 pthread_t tid; pthread_create(&tid, NULL, thread_son_func, NULL); sleep(1); //给线程创建留点时间 // 用 pthread_kill 向 \"子线程\" 发送信号 pthread_kill(tid, SIGUSR1); // 信号在子线程中处理 sleep(1); //给信号中打印留点时间 // 用 kill 向当前 \"进程\" 发送信号(可能被任意线程处理) kill(getpid(), SIGUSR1); // 信号可能在主线程或子线程中处理 pthread_join(tid, NULL); return 0;}
输出可能为:
子线程 140610751647488 启动信号 10 在线程 140610751647488 中被处理 // pthread_kill 发送,子线程处理信号 10 在线程 140610759944576 中被处理 // kill 发送,主线程处理(可能不同)
总结
kill
pthread_kill
SIGKILL
)简单说:kill
是 “给进程发信号”,pthread_kill
是 “给进程内的某个线程发信号”。