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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# kill — Send Signals to Processes
## Overview
`kill` sends signals to processes or lists available signals. This
implementation supports both numeric and named signal specifications,
real-time signals (`SIGRT`), and can be compiled as a shell built-in.
**Source**: `kill/kill.c` (single file)
**Origin**: BSD 4.4, University of California, Berkeley
**License**: BSD-3-Clause
## Synopsis
```
kill [-s signal_name] pid ...
kill -l [exit_status ...]
kill -signal_name pid ...
kill -signal_number pid ...
```
## Options
| Flag | Description |
|------|-------------|
| `-s signal` | Send the named signal |
| `-l` | List available signal names |
| `-signal_name` | Send named signal (e.g., `-TERM`) |
| `-signal_number` | Send signal by number (e.g., `-15`) |
## Source Analysis
### Signal Table
The signal table maps names to numbers using a macro-generated array:
```c
struct signal_entry {
const char *name;
int number;
};
#define SIGNAL_ENTRY(sig) { #sig, SIG##sig }
static const struct signal_entry signal_table[] = {
SIGNAL_ENTRY(HUP),
SIGNAL_ENTRY(INT),
SIGNAL_ENTRY(QUIT),
SIGNAL_ENTRY(ILL),
SIGNAL_ENTRY(TRAP),
SIGNAL_ENTRY(ABRT),
SIGNAL_ENTRY(EMT), /* If available */
SIGNAL_ENTRY(FPE),
SIGNAL_ENTRY(KILL),
SIGNAL_ENTRY(BUS),
SIGNAL_ENTRY(SEGV),
SIGNAL_ENTRY(SYS),
SIGNAL_ENTRY(PIPE),
SIGNAL_ENTRY(ALRM),
SIGNAL_ENTRY(TERM),
SIGNAL_ENTRY(URG),
SIGNAL_ENTRY(STOP),
SIGNAL_ENTRY(TSTP),
SIGNAL_ENTRY(CONT),
SIGNAL_ENTRY(CHLD),
SIGNAL_ENTRY(TTIN),
SIGNAL_ENTRY(TTOU),
SIGNAL_ENTRY(IO),
SIGNAL_ENTRY(XCPU),
SIGNAL_ENTRY(XFSZ),
SIGNAL_ENTRY(VTALRM),
SIGNAL_ENTRY(PROF),
SIGNAL_ENTRY(WINCH),
SIGNAL_ENTRY(INFO), /* If available */
SIGNAL_ENTRY(USR1),
SIGNAL_ENTRY(USR2),
/* ... */
};
```
### Key Functions
| Function | Purpose |
|----------|---------|
| `main()` | Parse options and dispatch signal or list |
| `normalize_signal_name()` | Canonicalize signal name (strip `SIG` prefix, uppercase) |
| `parse_signal_option_token()` | Parse `-SIGNAL` shorthand |
| `parse_signal_for_dash_s()` | Parse signal name/number for `-s` |
| `signal_name_for_number()` | Reverse lookup: number → name |
| `printsignals()` | List all signals (for `-l`) |
| `max_signal_number()` | Find highest valid signal |
| `parse_pid_argument()` | Parse and validate PID string |
### Signal Name Normalization
```c
static const char *
normalize_signal_name(const char *name)
{
/* Strip optional "SIG" prefix */
if (strncasecmp(name, "SIG", 3) == 0)
name += 3;
/* Case-insensitive lookup in signal_table */
for (size_t i = 0; i < SIGNAL_TABLE_SIZE; i++) {
if (strcasecmp(name, signal_table[i].name) == 0)
return signal_table[i].name;
}
return NULL;
}
```
### Parsing Signal Options
The option parsing handles three forms:
```c
/* Form 1: kill -s SIGNAL pid */
/* Form 2: kill -SIGNAL pid (dash prefix) */
/* Form 3: kill -NUMBER pid */
static int
parse_signal_option_token(const char *token)
{
/* Try as number first */
char *end;
long val = strtol(token, &end, 10);
if (*end == '\0' && val >= 0 && val <= max_signal_number())
return (int)val;
/* Try as name */
const char *name = normalize_signal_name(token);
if (name) {
/* Look up number from normalized name */
return number_for_name(name);
}
errx(2, "unknown signal: %s", token);
}
```
### Real-Time Signal Support
```c
/* SIGRTMIN+n and SIGRTMAX-n notation */
#ifdef SIGRTMIN
if (strncasecmp(name, "RTMIN", 5) == 0) {
int offset = (name[5] == '+') ? atoi(name + 6) : 0;
return SIGRTMIN + offset;
}
if (strncasecmp(name, "RTMAX", 5) == 0) {
int offset = (name[5] == '-') ? atoi(name + 6) : 0;
return SIGRTMAX - offset;
}
#endif
```
### Listing Signals
```c
static void
printsignals(FILE *fp)
{
int columns = 0;
for (int sig = 1; sig <= max_signal_number(); sig++) {
const char *name = signal_name_for_number(sig);
if (name) {
fprintf(fp, "%s", name);
if (++columns >= 8) {
fputc('\n', fp);
columns = 0;
} else {
fputc('\t', fp);
}
}
}
}
```
### Signal from Exit Status
When given an exit status with `-l`, the signal number is extracted:
```c
/* exit_status > 128 means killed by signal (exit_status - 128) */
if (exit_status > 128)
sig = exit_status - 128;
```
### Shell Built-in Integration
```c
#ifdef SHELL
/* When compiled into the shell (sh/), kill is a built-in */
/* Uses different error reporting and argument parsing */
int killcmd(int argc, char *argv[]);
#endif
```
## System Calls Used
| Syscall | Purpose |
|---------|---------|
| `kill(2)` | Send signal to process or process group |
## Examples
```sh
# Send SIGTERM (default)
kill 1234
# Send SIGKILL
kill -9 1234
kill -KILL 1234
kill -s KILL 1234
# Send to process group
kill -TERM -1234
# List all signals
kill -l
# Signal name from exit status
kill -l 137
# → KILL (137 - 128 = 9 = SIGKILL)
# Real-time signal
kill -s RTMIN+3 1234
```
## Exit Codes
| Code | Meaning |
|------|---------|
| 0 | All signals sent successfully |
| 1 | Error sending signal to at least one process |
| 2 | Usage error |
|