tgkill系统调用及示例

tgkill函数详解

  1. 函数介绍

tgkill函数是Linux系统中一个精确的进程控制函数,它的名字来源于”Thread Group Kill”(线程组杀死)。在Linux中,每个进程实际上是一个线程组,主线程的ID就是进程ID。tgkill允许你精确地向指定进程组中的特定线程发送信号。

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

可以把tgkill想象成一个”精确制导导弹”,它不仅能指定攻击哪个”军队”(进程组),还能精确指定攻击该军队中的哪个”士兵”(特定线程)。相比之下,传统的kill函数更像是”地毯式轰炸”,可能会影响整个进程组。

使用场景:

  • 多线程程序中精确控制特定线程

  • 线程调试和测试

  • 实现线程间通信机制

  • 精确的线程终止控制

  1. 函数原型
1
2
3
4
5
6
7
#include <sys/syscall.h>
#include <signal.h>

long syscall(SYS_tgkill, int tgid, int tid, int sig);
// 或者使用封装函数(如果系统提供)
int tgkill(int tgid, int tid, int sig);

注意:tgkill不是标准C库函数,通常需要通过系统调用来使用。

  1. 功能

tgkill函数的主要功能是向指定的线程组中的特定线程发送信号。它提供了比传统kill函数更高的精确度:

  • tgid(Thread Group ID):线程组ID,通常是进程ID

  • tid(Thread ID):线程ID,指定线程组中的具体线程

  • sig:要发送的信号

  1. 参数

tgid: 线程组ID(Thread Group ID)

  • 类型:int

  • 含义:目标线程所属的线程组ID,通常等于进程ID(PID)

tid: 线程ID(Thread ID)

  • 类型:int

  • 含义:线程组中具体线程的ID,必须是该线程组中的有效线程

sig: 信号编号

  • 类型:int

  • 含义:要发送给目标线程的信号,如SIGTERM、SIGKILL、SIGUSR1等

  1. 返回值
  • 成功: 返回0

失败: 返回-1,并设置errno错误码

  • EINVAL:信号编号无效

  • ESRCH:找不到指定的线程组或线程

  • EPERM:没有权限向目标线程发送信号

  • EAGAIN:系统资源不足

  1. 相似函数或关联函数
  • kill(): 向进程发送信号

  • pthread_kill(): 向POSIX线程发送信号

  • raise(): 向当前进程发送信号

  • signal()/sigaction(): 设置信号处理函数

  • getpid(): 获取当前进程ID

  • gettid(): 获取当前线程ID

  1. 示例代码

示例1:基础tgkill使用 - 精确控制线程

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>

// 获取线程ID的辅助函数
pid_t gettid(void) {
return syscall(SYS_gettid);
}

// 线程处理函数
void signal_handler(int sig) {
printf("线程 %d 收到信号 %d (%s)\n", gettid(), sig, strsignal(sig));
}

// 工作线程函数
void* worker_thread(void* arg) {
int thread_num = *(int*)arg;
pid_t tid = gettid();

printf("工作线程 %d 启动,线程ID: %d\n", thread_num, tid);

// 设置信号处理函数
signal(SIGUSR1, signal_handler);
signal(SIGTERM, signal_handler);

// 线程主循环
while(1) {
printf("线程 %d 正在工作...\n", tid);
sleep(2);
}

return NULL;
}

// tgkill系统调用封装
int tgkill(int tgid, int tid, int sig) {
return syscall(SYS_tgkill, tgid, tid, sig);
}

int main() {
pthread_t threads&#91;3];
int thread_nums&#91;3] = {1, 2, 3};
pid_t main_tid = gettid();
pid_t pids&#91;3];
int i;

printf("主线程ID: %d, 进程ID: %d\n", main_tid, getpid());

// 创建多个工作线程
for(i = 0; i < 3; i++) {
if(pthread_create(&threads&#91;i], NULL, worker_thread, &thread_nums&#91;i]) != 0) {
perror("创建线程失败");
exit(EXIT_FAILURE);
}
// 给线程一些时间启动
sleep(1);
pids&#91;i] = gettid(); // 这里简化处理,实际应该从线程中获取
}

// 让线程运行一段时间
sleep(3);

// 向特定线程发送自定义信号
printf("\n=== 向线程发送SIGUSR1信号 ===\n");
if(tgkill(getpid(), pids&#91;0], SIGUSR1) == 0) {
printf("成功向线程 %d 发送 SIGUSR1 信号\n", pids&#91;0]);
} else {
perror("tgkill发送信号失败");
}

sleep(2);

// 向另一个线程发送终止信号
printf("\n=== 向线程发送SIGTERM信号 ===\n");
if(tgkill(getpid(), pids&#91;1], SIGTERM) == 0) {
printf("成功向线程 %d 发送 SIGTERM 信号\n", pids&#91;1]);
} else {
perror("tgkill发送信号失败");
}

sleep(2);

// 演示错误处理
printf("\n=== 演示错误处理 ===\n");
if(tgkill(999999, 999999, SIGTERM) == -1) {
printf("向不存在的线程发送信号失败(预期行为): %s\n", strerror(errno));
}

// 等待几秒观察结果
sleep(3);

// 强制终止所有线程
printf("\n=== 终止所有线程 ===\n");
for(i = 0; i < 3; i++) {
tgkill(getpid(), pids&#91;i], SIGKILL);
}

return 0;
}

示例2:线程监控和管理

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>

#define MAX_THREADS 5

// 线程信息结构体
typedef struct {
pthread_t thread;
pid_t tid;
int id;
int running;
time_t start_time;
} thread_info_t;

thread_info_t threads&#91;MAX_THREADS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 获取线程ID
pid_t gettid(void) {
return syscall(SYS_gettid);
}

// 信号处理函数
void signal_handler(int sig) {
pid_t tid = gettid();
printf("&#91;信号处理] 线程 %d 收到信号: %s\n", tid, strsignal(sig));

if(sig == SIGUSR1) {
printf("&#91;信号处理] 线程 %d 将暂停工作\n", tid);
sleep(5);
printf("&#91;信号处理] 线程 %d 恢复工作\n", tid);
} else if(sig == SIGTERM) {
printf("&#91;信号处理] 线程 %d 准备退出\n", tid);
pthread_exit(NULL);
}
}

// 工作线程函数
void* worker_thread(void* arg) {
thread_info_t* info = (thread_info_t*)arg;
info->tid = gettid();
info->running = 1;
info->start_time = time(NULL);

printf("工作线程 %d 启动,系统线程ID: %d\n", info->id, info->tid);

// 设置信号处理
signal(SIGUSR1, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);

// 工作循环
while(info->running) {
printf("线程 %d 正在处理任务...\n", info->tid);
sleep(3);
}

printf("线程 %d 正常退出\n", info->tid);
return NULL;
}

// tgkill封装函数
int tgkill(int tgid, int tid, int sig) {
return syscall(SYS_tgkill, tgid, tid, sig);
}

// 显示所有线程状态
void show_thread_status() {
printf("\n=== 线程状态 ===\n");
pthread_mutex_lock(&mutex);
for(int i = 0; i < MAX_THREADS; i++) {
if(threads&#91;i].tid != 0) {
time_t uptime = time(NULL) - threads&#91;i].start_time;
printf("线程 %d: TID=%d, 状态=%s, 运行时间=%lds\n",
threads&#91;i].id, threads&#91;i].tid,
threads&#91;i].running ? "运行中" : "已停止",
uptime);
}
}
pthread_mutex_unlock(&mutex);
printf("================\n\n");
}

int main() {
int i;

printf("主线程监控程序启动,PID: %d\n", getpid());

// 初始化线程信息
memset(threads, 0, sizeof(threads));

// 创建工作线程
for(i = 0; i < MAX_THREADS; i++) {
threads&#91;i].id = i + 1;
if(pthread_create(&threads&#91;i].thread, NULL, worker_thread, &threads&#91;i]) != 0) {
perror("创建线程失败");
exit(EXIT_FAILURE);
}
printf("创建线程 %d\n", i + 1);
}

// 给线程一些启动时间
sleep(2);

// 显示初始状态
show_thread_status();

// 演示各种tgkill操作
printf("=== 演示tgkill操作 ===\n");

// 向第一个线程发送暂停信号
if(tgkill(getpid(), threads&#91;0].tid, SIGUSR1) == 0) {
printf("向线程 %d 发送暂停信号\n", threads&#91;0].tid);
}

sleep(1);

// 向第三个线程发送终止信号
if(tgkill(getpid(), threads&#91;2].tid, SIGTERM) == 0) {
printf("向线程 %d 发送终止信号\n", threads&#91;2].tid);
pthread_mutex_lock(&mutex);
threads&#91;2].running = 0;
pthread_mutex_unlock(&mutex);
}

sleep(2);
show_thread_status();

// 向所有线程发送中断信号
printf("向所有线程发送中断信号...\n");
pthread_mutex_lock(&mutex);
for(i = 0; i < MAX_THREADS; i++) {
if(threads&#91;i].tid != 0 && threads&#91;i].running) {
if(tgkill(getpid(), threads&#91;i].tid, SIGINT) == 0) {
printf("向线程 %d 发送中断信号\n", threads&#91;i].tid);
}
}
}
pthread_mutex_unlock(&mutex);

sleep(2);

// 等待所有线程结束
for(i = 0; i < MAX_THREADS; i++) {
if(threads&#91;i].tid != 0) {
pthread_join(threads&#91;i].thread, NULL);
printf("线程 %d 已结束\n", threads&#91;i].id);
}
}

printf("所有线程已安全退出,程序结束\n");
return 0;
}

示例3:错误处理和权限检查

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

// tgkill封装函数
int tgkill(int tgid, int tid, int sig) {
return syscall(SYS_tgkill, tgid, tid, sig);
}

// 错误处理函数
void handle_error(const char* operation) {
switch(errno) {
case EINVAL:
printf("%s 失败: 无效的信号编号\n", operation);
break;
case ESRCH:
printf("%s 失败: 找不到指定的线程或进程\n", operation);
break;
case EPERM:
printf("%s 失败: 权限不足\n", operation);
break;
default:
printf("%s 失败: %s\n", operation, strerror(errno));
break;
}
}

int main() {
pid_t my_pid = getpid();
pid_t my_tid = syscall(SYS_gettid);

printf("当前进程信息:\n");
printf(" 进程ID (PID): %d\n", my_pid);
printf(" 线程ID (TID): %d\n", my_tid);
printf(" 用户ID (UID): %d\n", getuid());
printf(" 有效用户ID (EUID): %d\n", geteuid());
printf("\n");

// 测试1: 向自己发送信号(应该成功)
printf("=== 测试1: 向当前线程发送信号 ===\n");
if(tgkill(my_pid, my_tid, SIGUSR1) == 0) {
printf("✓ 成功向当前线程发送信号\n");
} else {
handle_error("向当前线程发送信号");
}

// 测试2: 使用无效信号编号
printf("\n=== 测试2: 使用无效信号编号 ===\n");
if(tgkill(my_pid, my_tid, 999) == -1) {
handle_error("使用无效信号");
}

// 测试3: 向不存在的进程发送信号
printf("\n=== 测试3: 向不存在的进程发送信号 ===\n");
if(tgkill(999999, 999999, SIGTERM) == -1) {
handle_error("向不存在的进程发送信号");
}

// 测试4: 向不存在的线程发送信号
printf("\n=== 测试4: 向不存在的线程发送信号 ===\n");
if(tgkill(my_pid, 999999, SIGTERM) == -1) {
handle_error("向不存在的线程发送信号");
}

// 测试5: 发送各种标准信号
printf("\n=== 测试5: 发送标准信号 ===\n");
int signals&#91;] = {SIGHUP, SIGINT, SIGQUIT, SIGUSR1, SIGUSR2};
const char* signal_names&#91;] = {"SIGHUP", "SIGINT", "SIGQUIT", "SIGUSR1", "SIGUSR2"};
int num_signals = sizeof(signals) / sizeof(signals&#91;0]);

for(int i = 0; i < num_signals; i++) {
printf("发送 %s (信号 %d)... ", signal_names&#91;i], signals&#91;i]);
if(tgkill(my_pid, my_tid, signals&#91;i]) == 0) {
printf("成功\n");
} else {
printf("失败\n");
}
}

printf("\n=== 完成所有测试 ===\n");
printf("注意: 某些信号可能被系统忽略或有特殊处理\n");

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
# 编译示例1(需要链接pthread库)
gcc -o tgkill_example1 tgkill_example1.c -lpthread
./tgkill_example1

# 编译示例2
gcc -o tgkill_example2 tgkill_example2.c -lpthread
./tgkill_example2

# 编译示例3
gcc -o tgkill_example3 tgkill_example3.c
./tgkill_example3

重要注意事项

权限要求: tgkill需要适当的权限才能向目标线程发送信号

线程ID获取: 需要在线程内部调用syscall(SYS_gettid)获取真实的线程ID

错误处理: 必须检查返回值并适当处理错误

信号安全: 在信号处理函数中只能调用异步信号安全的函数

跨平台兼容性: tgkill是Linux特有的系统调用,不适用于其他操作系统

通过这些示例,你可以理解tgkill在精确线程控制方面的强大功能,它为多线程程序提供了精细化的控制能力。

data-ad-format="auto" data-full-width-responsive="true">