Skip to content

Commit 2e00404

Browse files
committed
Use pidfds & process_mrelease
Closes #266
1 parent b0ec8b7 commit 2e00404

File tree

4 files changed

+72
-17
lines changed

4 files changed

+72
-17
lines changed

kill.c

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
#include <ctype.h>
66
#include <dirent.h>
77
#include <errno.h>
8+
#include <limits.h>
89
#include <signal.h>
910
#include <stdio.h>
1011
#include <stdlib.h>
1112
#include <string.h>
13+
#include <sys/syscall.h> /* Definition of SYS_* constants */
1214
#include <time.h>
1315
#include <unistd.h>
14-
#include <limits.h>
1516

1617
#include "globals.h"
1718
#include "kill.h"
@@ -126,33 +127,59 @@ static void notify_process_killed(const poll_loop_args_t* args, const procinfo_t
126127
}
127128
}
128129

130+
int send_signal(int pidfd_or_negative_pgid, int sig)
131+
{
132+
if (pidfd_or_negative_pgid >= 0) {
133+
return (int)syscall(SYS_pidfd_send_signal, pidfd_or_negative_pgid, sig, NULL, 0);
134+
} else {
135+
return kill(pidfd_or_negative_pgid, sig);
136+
}
137+
}
138+
129139
/*
130-
* Send the selected signal to "pid" and wait for the process to exit
140+
* Send the selected signal to `victim` and wait for it to exit
131141
* (max 10 seconds)
132142
*/
133-
int kill_wait(const poll_loop_args_t* args, pid_t pid, int sig)
143+
int kill_wait(const poll_loop_args_t* args, const procinfo_t* victim, int sig)
134144
{
135145
if (args->dryrun && sig != 0) {
136146
warn("dryrun, not actually sending any signal\n");
137147
return 0;
138148
}
149+
int pidfd_or_negative_pgid = victim->pidfd;
150+
int pid_or_negative_pgid = victim->pid;
139151
const unsigned poll_ms = 100;
140152
if (args->kill_process_group) {
141-
int res = getpgid(pid);
153+
int res = getpgid(victim->pid);
142154
if (res < 0) {
143155
return res;
144156
}
145-
pid = -res;
146-
warn("killing whole process group %d (-g flag is active)\n", res);
157+
pidfd_or_negative_pgid = -res;
158+
pid_or_negative_pgid = -res;
159+
if (sig != 0) {
160+
warn("killing whole process group %d (-g flag is active)\n", res);
161+
}
147162
}
148-
int res = kill(pid, sig);
163+
int res = send_signal(pidfd_or_negative_pgid, sig);
149164
if (res != 0) {
150165
return res;
151166
}
152167
/* signal 0 does not kill the process. Don't wait for it to exit */
153168
if (sig == 0) {
154169
return 0;
155170
}
171+
172+
#ifdef __NR_process_mrelease
173+
if (pidfd_or_negative_pgid >= 0) {
174+
int res = (int)syscall(__NR_process_mrelease, pidfd_or_negative_pgid, 0);
175+
if (res != 0) {
176+
warn("process_mrelease pidfd=%d failed: %s\n", pidfd_or_negative_pgid, strerror(errno));
177+
} else {
178+
debug("process_mrelease success\n");
179+
}
180+
}
181+
#endif
182+
156183
for (unsigned i = 0; i < 100; i++) {
157184
float secs = (float)(i * poll_ms) / 1000;
158185
// We have sent SIGTERM but now have dropped below SIGKILL limits.
@@ -162,7 +189,7 @@ int kill_wait(const poll_loop_args_t* args, pid_t pid, int sig)
162189
print_mem_stats(debug, m);
163190
if (m.MemAvailablePercent <= args->mem_kill_percent && m.SwapFreePercent <= args->swap_kill_percent) {
164191
sig = SIGKILL;
165-
res = kill(pid, sig);
192+
res = send_signal(pidfd_or_negative_pgid, sig);
166193
// kill first, print after
167194
warn("escalating to SIGKILL after %.1f seconds\n", secs);
168195
if (res != 0) {
@@ -173,7 +200,7 @@ int kill_wait(const poll_loop_args_t* args, pid_t pid, int sig)
173200
meminfo_t m = parse_meminfo();
174201
print_mem_stats(printf, m);
175202
}
176-
if (!is_alive(pid)) {
203+
if (!is_alive(pid_or_negative_pgid)) {
177204
warn("process exited after %.1f seconds\n", secs);
178205
return 0;
179206
}
@@ -268,6 +295,15 @@ bool is_larger(const poll_loop_args_t* args, const procinfo_t* victim, procinfo_
268295
}
269296
cur->uid = res;
270297
}
298+
{
299+
int res = (int)syscall(SYS_pidfd_open, cur->pid, 0);
300+
if (res < 0) {
301+
// can happen if process has already exited
302+
debug("pid %d: error opening pidfd: %s\n", cur->pid, strerror(errno));
303+
return false;
304+
}
305+
cur->pidfd = res;
306+
}
271307
return true;
272308
}
273309

@@ -296,7 +332,10 @@ procinfo_t find_largest_process(const poll_loop_args_t* args)
296332
clock_gettime(CLOCK_MONOTONIC, &t0);
297333
}
298334

299-
procinfo_t victim = { 0 };
335+
procinfo_t victim = {
336+
.pidfd = -1,
337+
/* omitted fields are set to zero */
338+
};
300339
while (1) {
301340
errno = 0;
302341
struct dirent* d = readdir(procdir);
@@ -313,6 +352,7 @@ procinfo_t find_largest_process(const poll_loop_args_t* args)
313352

314353
procinfo_t cur = {
315354
.pid = (int)strtol(d->d_name, NULL, 10),
355+
.pidfd = -1,
316356
.uid = -1,
317357
.badness = -1,
318358
.VmRSSkiB = -1,
@@ -326,6 +366,12 @@ procinfo_t find_largest_process(const poll_loop_args_t* args)
326366

327367
if (larger) {
328368
debug(" <--- new victim\n");
369+
if (victim.pidfd >= 0) {
370+
int res = close(victim.pidfd);
371+
if (res != 0) {
372+
warn("%s: pid %d: error closing pidfd %d: %s\n", __func__, victim.pid, victim.pidfd, strerror(errno));
373+
}
374+
}
329375
victim = cur;
330376
} else {
331377
debug("\n");
@@ -353,7 +399,7 @@ procinfo_t find_largest_process(const poll_loop_args_t* args)
353399
* Kill the victim process, wait for it to exit, send a gui notification
354400
* (if enabled).
355401
*/
356-
void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victim)
402+
void kill_process(const poll_loop_args_t* args, int sig, procinfo_t* victim)
357403
{
358404
if (victim->pid <= 0) {
359405
warn("Could not find a process to kill. Sleeping 1 second.\n");
@@ -378,9 +424,17 @@ void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victi
378424
sig_name, victim->pid, victim->uid, victim->name, victim->badness, victim->VmRSSkiB / 1024);
379425
}
380426

381-
int res = kill_wait(args, victim->pid, sig);
427+
int res = kill_wait(args, victim, sig);
382428
int saved_errno = errno;
383429

430+
if (victim->pidfd >= 0) {
431+
int res = close(victim->pidfd);
432+
if (res != 0) {
433+
warn("%s: pid %d: error closing pidfd %d: %s\n", __func__, victim->pid, victim->pidfd, strerror(errno));
434+
}
435+
victim->pidfd = -1;
436+
}
437+
384438
// Send the GUI notification AFTER killing a process. This makes it more likely
385439
// that there is enough memory to spawn the notification helper.
386440
if (sig != 0) {

kill.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ typedef struct {
2929
bool dryrun;
3030
} poll_loop_args_t;
3131

32-
void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victim);
32+
void kill_process(const poll_loop_args_t* args, int sig, procinfo_t* victim);
3333
procinfo_t find_largest_process(const poll_loop_args_t* args);
3434

3535
#endif

meminfo.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ meminfo_t parse_meminfo()
123123
return m;
124124
}
125125

126-
bool is_alive(int pid)
126+
bool is_alive(int pid_or_negative_pgid)
127127
{
128128
// whole process group (-g flag)?
129-
if (pid < 0) {
129+
if (pid_or_negative_pgid < 0) {
130130
// signal 0 does nothing, but we do get an error when the process
131131
// group does not exist.
132-
int res = kill(pid, 0);
132+
int res = kill(pid_or_negative_pgid, 0);
133133
if (res == 0) {
134134
return true;
135135
}
@@ -138,7 +138,7 @@ bool is_alive(int pid)
138138

139139
char buf[PATH_LEN] = { 0 };
140140
// Read /proc/[pid]/stat
141-
snprintf(buf, sizeof(buf), "%s/%d/stat", procdir_path, pid);
141+
snprintf(buf, sizeof(buf), "%s/%d/stat", procdir_path, pid_or_negative_pgid);
142142
FILE* f = fopen(buf, "r");
143143
if (f == NULL) {
144144
// Process is gone - good.

meminfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef struct {
2121

2222
typedef struct procinfo {
2323
int pid;
24+
int pidfd;
2425
int uid;
2526
int badness;
2627
int oom_score_adj;

0 commit comments

Comments
 (0)