proc: test /proc/*/fd a bit (+ PF_KTHREAD is ABI!)
Subsystems (2)
PATCH 1/1 proc: test /proc/*/fd a bit (+ PF_KTHREAD is ABI!)
* Test lookup in /proc/self/fd.
"map_files" lookup story showed that lookup is not that simple.
* Test that all those symlinks open the same file. Check with (st_dev, st_info).
* Test that kernel threads do not have anything in their /proc/*/fd/ directory.
Now this is where things get interesting.
First, kernel threads aren't pinned by /proc/self or equivalent, thus some "atomicity" is required.
Second, ->comm can contain whitespace and ')'.
No, they are not escaped.
Third, the only reliable way to check if process is kernel thread appears to be field #9 in /proc/*/stat.
This field is struct task_struct::flags in decimal! Check is done by testing PF_KTHREAD flags like we do in kernel.
PF_KTREAD value is a part of userspace ABI !!!
Other methods for determining kernel threadness are not reliable:
* RSS can be 0 if everything is swapped, even while reading from /proc/self.
* ->total_vm CAN BE ZERO if process is finishing
munmap(NULL, whole address space);
* /proc/*/maps and similar files can be empty because unmapping everything works. Read returning 0 can't distinguish between kernel thread and such suicide process.
---
tools/testing/selftests/proc/.gitignore | 3 tools/testing/selftests/proc/Makefile | 5 tools/testing/selftests/proc/fd-001-lookup.c | 168 +++++++++++++++++++++++ tools/testing/selftests/proc/fd-002-posix-eq.c | 57 ++++++++ tools/testing/selftests/proc/fd-003-kthread.c | 178 +++++++++++++++++++++++++ tools/testing/selftests/proc/proc-uptime.h | 16 -- tools/testing/selftests/proc/proc.h | 39 +++++ tools/testing/selftests/proc/read.c | 17 -- 8 files changed, 451 insertions(+), 32 deletions(-)
tools/testing/selftests/proc/.gitignore +3 -0
@@ -1,3 +1,6 @@ +/fd-001-lookup +/fd-002-posix-eq +/fd-003-kthread /proc-loadavg-001 /proc-self-map-files-001 /proc-self-map-files-002
tools/testing/selftests/proc/Makefile +4 -1
@@ -1,6 +1,9 @@ -CFLAGS += -Wall -O2 +CFLAGS += -Wall -O2 -Wno-unused-function TEST_GEN_PROGS := +TEST_GEN_PROGS += fd-001-lookup +TEST_GEN_PROGS += fd-002-posix-eq +TEST_GEN_PROGS += fd-003-kthread TEST_GEN_PROGS += proc-loadavg-001 TEST_GEN_PROGS += proc-self-map-files-001 TEST_GEN_PROGS += proc-self-map-files-002
new file mode 100644
tools/testing/selftests/proc/fd-001-lookup.c new file +168 -0
168 lines not on this page · raw message
new file mode 100644
tools/testing/selftests/proc/fd-002-posix-eq.c new file +57 -0
57 lines not on this page · raw message
new file mode 100644
tools/testing/selftests/proc/fd-003-kthread.c new file +178 -0
178 lines not on this page · raw message
tools/testing/selftests/proc/proc-uptime.h +1 -15
@@ -20,21 +20,7 @@#include <stdlib.h> #include <unistd.h> -static unsigned long long xstrtoull (const char *p,char **end) -{ -if (*p =='0' ) { - *end = (char *)p +1 ; -return 0 ; - }else if ('1' <= *p && *p <='9' ) { -unsigned long long val; - - errno =0 ; - val =strtoull (p, end,10 ); -assert (errno ==0 ); -return val; - }else -assert (0 ); -} +#include "proc.h" static void proc_uptime (int fd,uint64_t *uptime,uint64_t *idle) {
new file mode 100644
tools/testing/selftests/proc/proc.h new file +39 -0
@@ -0,0 +1,39 @@ +#pragma once +#undef NDEBUG +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +static inline bool streq (const char *s1,const char *s2) +{ +return strcmp (s1, s2) ==0 ; +} + +static unsigned long long xstrtoull (const char *p,char **end) +{ +if (*p =='0' ) { + *end = (char *)p +1 ; +return 0 ; + }else if ('1' <= *p && *p <='9' ) { +unsigned long long val; + + errno =0 ; + val =strtoull (p, end,10 ); +assert (errno ==0 ); +return val; + }else +assert (0 ); +} + +static struct dirent *xreaddir (DIR *d) +{ +struct dirent *de; + + errno =0 ; + de =readdir (d); +assert (de || errno ==0 ); +return de; +}
tools/testing/selftests/proc/read.c +1 -16
@@ -31,22 +31,7 @@#include <fcntl.h> #include <unistd.h> -static inline bool streq (const char *s1,const char *s2) -{ -return strcmp (s1, s2) ==0 ; -} - -static struct dirent *xreaddir (DIR *d) -{ -struct dirent *de; - - errno =0 ; - de =readdir (d); -if (!de && errno !=0 ) { -exit (1 ); - } -return de; -} +#include "proc.h" static void f_reg (DIR *d,const char *filename) {
Quoted 17 lines · On Sat, 5 May 2018 03:04:14 +0300 Alexey Dobriyan <[email protected]> wrote:
* Test lookup in
/proc/self/fd.
"map_files" lookup story showed that lookup is not that simple.* Test that all those symlinks open the same file. Check with (
st_dev,st_info).* Test that kernel threads do not have anything in their
/proc/*/fd/directory.Now this is where things get interesting.
First, kernel threads aren't pinned by
/proc/selfor equivalent, thus some "atomicity" is required.Second, ->comm can contain whitespace and ')'.
No, they are not escaped.
Quoted 7 lines
Third, the only reliable way to check if process is kernel thread appears to be field #9 in
/proc/*/stat.This field is struct
task_struct::flagsin decimal! Check is done by testingPF_KTHREADflags like we do in kernel.
PF_KTREADvalue is a part of userspace ABI !!!
erk. Well if there's a need the we could export and support some stable interface. I wonder how ps determines this.
Quoted 12 lines
Other methods for determining kernel threadness are not reliable:
* RSS can be 0 if everything is swapped, even while reading from/proc/self.* ->total_vm CAN BE ZERO if process is finishing
munmap(NULL, whole address space);*
/proc/*/mapsand similar files can be empty because unmapping everything works. Read returning 0 can't distinguish between kernel thread and such suicide process.
Quoted 9 lines · Andrew Morton
Quoted 4 lines · On Sat, 5 May 2018 03:04:14 +0300 Alexey Dobriyan <[email protected]> wrote:
This field is struct
task_struct::flagsin decimal! Check is done by testingPF_KTHREADflags like we do in kernel.
PF_KTREADvalue is a part of userspace ABI !!!erk. Well if there's a need the we could export and support some stable interface. I wonder how ps determines this.
Turned out ps (from procps-ng) reads /proc/*/cmdline and if read() returns 0 prints [] brackets. This is unreliable as
execve("", NULL, NULL);exists and processes can empty their argv/envp area.
It can do sorting based on "flags" field, so we hopefully can just mask most of the bits.
At least it deals with ->comm containing ')' right.