Skip to content

Commit

Permalink
Split up finding and killing
Browse files Browse the repository at this point in the history
Split kill_largest_process into find_largest_process + kill_process.

#205
  • Loading branch information
rfjakob committed Sep 5, 2020
1 parent a2470fb commit 3a07229
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 32 deletions.
53 changes: 30 additions & 23 deletions kill.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,29 +113,27 @@ int kill_wait(const poll_loop_args_t* args, pid_t pid, int sig)
}

/*
* Find the process with the largest oom_score and kill it.
* Find the process with the largest oom_score.
*/
void kill_largest_process(const poll_loop_args_t* args, int sig)
procinfo_t find_largest_process(const poll_loop_args_t* args)
{
struct procinfo victim = { 0 };
struct timespec t0 = { 0 }, t1 = { 0 };
DIR* procdir = opendir("/proc");
if (procdir == NULL) {
fatal(5, "%s: could not open /proc: %s", __func__, strerror(errno));
}

struct timespec t0 = { 0 }, t1 = { 0 };
if (enable_debug) {
clock_gettime(CLOCK_MONOTONIC, &t0);
}

DIR* procdir = opendir("/proc");
if (procdir == NULL) {
fatal(5, "Could not open /proc: %s", strerror(errno));
}

int candidates = 0;
procinfo_t victim = { 0 };
while (1) {
errno = 0;
struct dirent* d = readdir(procdir);
if (d == NULL) {
if (errno != 0)
warn("userspace_kill: readdir error: %s", strerror(errno));
warn("%s: readdir error: %s", __func__, strerror(errno));
break;
}

Expand All @@ -144,7 +142,7 @@ void kill_largest_process(const poll_loop_args_t* args, int sig)
if (!isnumeric(d->d_name))
continue;

struct procinfo cur = {
procinfo_t cur = {
.pid = (int)strtol(d->d_name, NULL, 10),
.uid = -1,
.badness = -1,
Expand Down Expand Up @@ -192,7 +190,6 @@ void kill_largest_process(const poll_loop_args_t* args, int sig)
}

debug(" badness %3d", cur.badness);
candidates++;

if (cur.badness < victim.badness) {
// skip "type 1", encoded as 1 space
Expand Down Expand Up @@ -261,12 +258,28 @@ void kill_largest_process(const poll_loop_args_t* args, int sig)
} // end of while(1) loop
closedir(procdir);

if (candidates <= 1 && victim.pid == getpid()) {
warn("Only found myself (pid %d) in /proc. Do you use hidpid? See https://github.com/rfjakob/earlyoom/wiki/proc-hidepid\n",
victim.pid);
victim.pid = 0;
if (enable_debug) {
clock_gettime(CLOCK_MONOTONIC, &t1);
long delta = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_nsec - t0.tv_nsec) / 1000;
debug("selecting victim took %ld.%03ld ms\n", delta / 1000, delta % 1000);
}

if (victim.pid == getpid()) {
warn("%s: selected myself (pid %d). Do you use hidpid? See https://github.com/rfjakob/earlyoom/wiki/proc-hidepid\n",
__func__, victim.pid);
// zeroize victim struct
victim = (const procinfo_t) { 0 };
}

return victim;
}

/*
* Kill the victim process, wait for it to exit, send a gui notification
* (if enabled).
*/
void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t victim)
{
if (victim.pid <= 0) {
warn("Could not find a process to kill. Sleeping 1 second.\n");
if (args->notify) {
Expand All @@ -276,12 +289,6 @@ void kill_largest_process(const poll_loop_args_t* args, int sig)
return;
}

if (enable_debug) {
clock_gettime(CLOCK_MONOTONIC, &t1);
long delta = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_nsec - t0.tv_nsec) / 1000;
debug("selecting victim took %ld.%03ld ms\n", delta / 1000, delta % 1000);
}

char* sig_name = "?";
if (sig == SIGTERM) {
sig_name = "SIGTERM";
Expand Down
5 changes: 4 additions & 1 deletion kill.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <regex.h>
#include <stdbool.h>

#include "meminfo.h"

typedef struct {
/* if the available memory AND swap goes below these percentages,
* we start killing processes */
Expand All @@ -25,6 +27,7 @@ typedef struct {
bool dryrun;
} poll_loop_args_t;

void kill_largest_process(const poll_loop_args_t* args, int sig);
void kill_process(const poll_loop_args_t* args, int sig, procinfo_t victim);
procinfo_t find_largest_process(const poll_loop_args_t* args);

#endif
8 changes: 6 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,10 @@ int main(int argc, char* argv[])
* calling mlockall()
*/
debug("dry-running kill_largest_process()...\n");
kill_largest_process(&args, 0);
{
procinfo_t victim = find_largest_process(&args);
kill_process(&args, 0, victim);
}

int err = mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
// kernels older than 4.4 don't support MCL_ONFAULT. Retry without it.
Expand Down Expand Up @@ -399,7 +402,8 @@ static void poll_loop(const poll_loop_args_t* args)
sig = SIGTERM;
}
if (sig) {
kill_largest_process(args, sig);
procinfo_t victim = find_largest_process(args);
kill_process(args, sig, victim);
} else if (args->report_interval_ms && report_countdown_ms <= 0) {
print_mem_stats(printf, m);
report_countdown_ms = args->report_interval_ms;
Expand Down
4 changes: 2 additions & 2 deletions meminfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ typedef struct {
double SwapFreePercent; // percent of total swap that is free
} meminfo_t;

struct procinfo {
typedef struct procinfo {
int pid;
int uid;
int badness;
long long VmRSSkiB;
char name[PATH_LEN];
};
} procinfo_t;

meminfo_t parse_meminfo();
bool is_alive(int pid);
Expand Down
11 changes: 9 additions & 2 deletions testsuite_c_wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,16 @@ func parse_meminfo() C.meminfo_t {
return C.parse_meminfo()
}

func kill_largest_process() {
func find_largest_process() {
var args C.poll_loop_args_t
C.kill_largest_process(&args, 0)
C.find_largest_process(&args)
}

func kill_process() {
var args C.poll_loop_args_t
var victim C.procinfo_t
victim.pid = 1
C.kill_process(&args, 0, victim)
}

func get_oom_score(pid int) int {
Expand Down
10 changes: 8 additions & 2 deletions testsuite_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,15 @@ func Benchmark_parse_meminfo(b *testing.B) {
}
}

func Benchmark_kill_largest_process(b *testing.B) {
func Benchmark_kill_process(b *testing.B) {
for n := 0; n < b.N; n++ {
kill_largest_process()
kill_process()
}
}

func Benchmark_find_largest_process(b *testing.B) {
for n := 0; n < b.N; n++ {
find_largest_process()
}
}

Expand Down

0 comments on commit 3a07229

Please # to comment.