Welcome to /proc
What is /proc? It is a pseudo directory present in all Linux systems which provides information about the system. It was originally designed to convey process information (hence the name), but now conveys a wide array of system information.
Note that I referred to /proc as a “pseudo directory”. It doesn’t actually exist (on disk). Instead, the Linux kernel creates this pseudo filesystem in memory, so that we can access it as if it were a directory on disk.
Let’s take a look.
$ ls /proc
1 13401 2 25 34 4478 50 5336 61 674 75 818 88 98 device-tree ioports locks slabinfo tty
10 14 21 26 35 45 5037 5337 62 676 77 844 89 asound devices irq meminfo softirqs uptime
1084 142 22 26222 3611 450 51 54 63 679 774 85 9 buddyinfo diskstats kallsyms misc stat vc-cma
1088 144 223 27 434 46 5136 5464 64 68 775 8580 90 bus driver key-users modules swaps version
11 15 228 28 435 47 5183 55 65 69 78 8588 91 cgroups execdomains keys mounts sys vmallocinfo
1105 152 229 29 438 48 52 56 66 7 79 8593 92 cmdline fb kmsg net sysrq-trigger vmstat
1127 17 23 3 439 481 53 57 67 70 8 86 93 consoles filesystems kpagecgroup pagetypeinfo sysvipc zoneinfo
1163 18 23307 31 44 485 5322 58 670 71 80 87 939 cpu fs kpagecount partitions thread-self
1238 18779 237 32 442 49 5323 59 671 72 801 870 96 cpuinfo interrupts kpageflags sched_debug timer_list
13 19 24 33 4477 5 5332 60 672 73 810 8719 97 crypto iomem loadavg self timer_stats
As you notice, there’s a whole slew of numeric directories. These contain information about the process with the corresponding id. There’s also a wide variety of other information, such as memory information (meminfo), load average statistics (loadavg), kernel version, gcc version, and Linux distribution (version), virtual memory statistics (vmstat), and a wide variety of other kernel and hardware information.
So, what’s in a process? Let’s take a peek.
$ ls /proc/$( pgrep -f -n tmux)
attr auxv clear_refs comm cpuset environ fd gid_map limits map_files mem mounts net numa_maps oom_score
pagemap projid_map sched sessionid smaps stat status task uid_map autogroup cgroup cmdline coredump_filter cwd exe
fdinfo io loginuid maps mountinfo mountstats ns oom_adj oom_score_adj personality root schedstat setgroups stack statm syscall
timers wchan
You can check out the status of a process in status.
$ cat /proc/$( pgrep -f -n tmux) /status
Name: tmux
State: S ( sleeping)
Tgid: 4143
Ngid: 0
Pid: 4143
PPid: 1
TracerPid: 0
Uid: 1001 1001 1001 1001
Gid: 1001 1001 1001 1001
FDSize: 64
Groups: 10 1001
VmPeak: 130100 kB
VmSize: 129840 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 6136 kB
VmRSS: 5516 kB
RssAnon: 4220 kB
RssFile: 1296 kB
RssShmem: 0 kB
VmData: 4404 kB
VmStk: 140 kB
VmExe: 448 kB
VmLib: 2700 kB
VmPTE: 80 kB
VmSwap: 160 kB
Threads: 1
SigQ: 0/127679
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000081802
SigCgt: 0000000188034201
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000001fffffffff
CapAmb: 0000000000000000
Seccomp: 0
Cpus_allowed: ff
Cpus_allowed_list: 0-7
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 1246008
nonvoluntary_ctxt_switches: 10550
In status you can see the state (sleeping), the process id (4143), the parent process id (1), ownership (user/group 1001), memory sizes, the number of threads, and many other details. It contains much of what is in stat and mem (status and memory information respectively), but in more human readable form.
You can also see what files the process is referencing in the fd directory. Here are the open file descriptors for my Plex Media Server.
$ cd /proc/$( pgrep -f -n 'Plex Media Server' )
$ sudo ls -l fd
total 0
lr-x------ 1 plex nogroup 64 Apr 28 00:44 0 -> /dev/null
l-wx------ 1 plex nogroup 64 Apr 28 00:44 1 -> /dev/null
l-wx------ 1 plex nogroup 64 Apr 28 00:44 10 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/Plex Media Server.log
lrwx------ 1 plex nogroup 64 Apr 28 00:44 11 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.dlna.db-shm
lrwx------ 1 plex nogroup 64 Apr 28 00:44 12 -> socket:[11739]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 13 -> socket:[11740]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 14 -> socket:[11741]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 15 -> socket:[11742]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 16 -> socket:[11743]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 17 -> socket:[11744]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 18 -> socket:[11746]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 19 -> socket:[11747]
l-wx------ 1 plex nogroup 64 Apr 28 00:44 2 -> /dev/null
lrwx------ 1 plex nogroup 64 Apr 28 00:44 20 -> socket:[11748]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 21 -> socket:[11749]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 22 -> socket:[11750]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 23 -> socket:[11751]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 24 -> socket:[11752]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 25 -> socket:[11753]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 26 -> socket:[11754]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 27 -> socket:[11755]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 28 -> socket:[11756]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 29 -> socket:[11757]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 3 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/Plex DLNA Server Neptune.log
lrwx------ 1 plex nogroup 64 Apr 28 00:44 30 -> socket:[11758]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 31 -> socket:[11759]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 32 -> socket:[11760]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 33 -> socket:[65881]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 34 -> socket:[65882]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 35 -> socket:[65883]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 4 -> anon_inode:[eventfd]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 5 -> anon_inode:[eventpoll]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 56 -> socket:[10045]
lr-x------ 1 plex nogroup 64 Apr 28 00:44 57 -> pipe:[10038]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 58 -> socket:[10044]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 59 -> socket:[10050]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 6 -> anon_inode:[timerfd]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 60 -> socket:[10053]
l-wx------ 1 plex nogroup 64 Apr 28 00:44 61 -> pipe:[10039]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 62 -> socket:[11730]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 63 -> socket:[10058]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 64 -> socket:[11731]
l-wx------ 1 plex nogroup 64 Apr 28 00:44 7 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/Plex DLNA Server.log
lr-x------ 1 plex nogroup 64 Apr 28 00:44 72 -> /dev/urandom
lrwx------ 1 plex nogroup 64 Apr 28 00:44 8 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.dlna.db
lrwx------ 1 plex nogroup 64 Apr 28 00:44 82 -> socket:[10072]
lrwx------ 1 plex nogroup 64 Apr 28 00:44 9 -> /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.dlna.db-wal
Using the proc filesystem, we can even access and recover files. Let’s get the tail of the log for file descriptor 10 (Plex Media Server.log).
$ sudo cat fd/10 | tail
Apr 28, 2017 00:20:19.835 [ 0x731ff400] DEBUG - NAT: UPnP, not an IGD: <http://192.168.0.1:49152/wps_device.xml>.
Apr 28, 2017 00:20:19.837 [ 0x731ff400] DEBUG - NAT: UPnP, found device <http://192.168.0.1:49152/wps_device.xml> with private address <192.168.0.101>
Apr 28, 2017 00:20:19.838 [ 0x731ff400] DEBUG - NAT: UPnP, not an IGD: <http://192.168.0.1:49152/wps_device.xml>.
Apr 28, 2017 00:20:19.840 [ 0x731ff400] DEBUG - NAT: UPnP, found device <http://192.168.0.1:1900/igd.xml> with private address <192.168.0.101>
Apr 28, 2017 00:20:19.845 [ 0x731ff400] DEBUG - NAT: UPnP, usable device <http://192.168.0.1:1900/igd.xml> with private address <192.168.0.101>.
Apr 28, 2017 00:20:19.847 [ 0x731ff400] DEBUG - NAT: UPnP, public address is XXX.XXX.XXX.XXX
Apr 28, 2017 00:20:19.850 [ 0x680fb400] DEBUG - PublicAddressManager: Mapping succeeded for 192.168.0.101:23979.
Apr 28, 2017 00:20:19.854 [ 0x731ff400] DEBUG - MyPlex: Last published value didn't change, we' re done .
Apr 28, 2017 00:20:19.855 [ 0x680fb400] DEBUG - MyPlex: Last published value didn't change, we' re done .
Apr 28, 2017 00:50:17.467 [ 0x680fb400] DEBUG - Sync: uploadStatus
We can also check out the environment of the process via environ.
$ sudo cat environ
INFINALITY_FT_AUTOHINT_HORIZONTAL_STEM_DARKEN_STRENGTH = 10^@MAIL= /var/mail/plex^@USER= plex^@INFINALITY_FT_FRINGE_FILTER_STRENGTH= 0^@SHLVL= 1^@LD_LIBRARY_PATH= /usr/lib/plexmediaserver^@HOME= /var/lib/plexmediaserver^@PLEX_MEDIA_SERVER_TMPDIR= /tmp^@OLDPWD= /var/lib/plexmediaserver^@INFINALITY_FT_CONTRAST= 0^@INFINALIT Y_FT_USE_VARIOUS_TWEAKS = true ^@INFINALITY_FT_GAMMA_CORRECTION= 0 100^@INFINALITY_FT_WINDOWS_STYLE_SHARPENING_STRENGTH= 10^@INFINALITY_FT_STEM_ALIGNMENT_STRENGTH= 25^@TMPDIR= /tmp^@LOGNAME= plex^@_= /usr/sbin/start_pms^@XDG_SESSION_ID= c1^@INFINALITY_FT_AUTOHINT_VERTICAL_STEM_DARKEN_STRENGTH= 25^@PATH= /usr/local/sbin:/us r/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games^@XDG_RUNTIME_DIR= /run/user/117^@PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR= /var/lib/plexmediaserver/Library/Application Support^@INFINALITY_FT_GLOBAL_EMBOLDEN_X_VALUE= 0^@LANG= en_GB.UTF-8^@INFINALITY_FT_AUTOHINT_SNAP_STEM_HEIGHT= 100^@INFINALITY _FT_GLOBAL_EMBOLDEN_Y_VALUE = 0^@INFINALITY_FT_CHROMEOS_STYLE_SHARPENING_STRENGTH= 0^@SHELL= /bin/bash^@PLEX_MEDIA_SERVER_MAX_STACK_SIZE= 3000^@INFINALITY_FT_BRIGHTNESS= 0^@INFINALITY_FT_STEM_SNAPPING_SLIDING_SCALE= 40^@INFINALITY_FT_STEM_FITTING_STRENGTH= 25^@INFINALITY_FT_GRAYSCALE_FILTER_STRENGTH= 0^@INFINALITY_FT_US E_KNOWN_SETTINGS_ON_SELECTED_FONTS = true ^@PWD= /usr/lib/plexmediaserver^@INFINALITY_FT_AUTOHINT_INCREASE_GLYPH_HEIGHTS= true ^@PLEX_MEDIA_SERVER_HOME= /usr/lib/plexmediaserver^@INFINALITY_FT_BOLD_EMBOLDEN_X_VALUE= 0^@INFINALITY_FT_FILTER_PARAMS= 11 22 38 22 11^@INFINALITY_FT_BOLD_EMBOLDEN_Y_VALUE= 0^@PLEX_MEDIA_SERVER_ MAX_PLUGIN_PROCS = 6^@
We can inspect the command with which the process was launched via cmdline.
$ sudo cat cmdline
./Plex Media Server
We can also take a look at the various kernel level limits for a process in limits.
$ sudo cat limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 7336 7336 processes
Max open files 65536 65536 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 7336 7336 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
Wrapping up
As you can see, there’s all kinds of cool information in /proc!
Many utilities (ps, top, lspci, etc.) get their information from the /proc filesystem.
Let’s take a look at the files ps opens when it runs on my Raspberry Pi.
$ strace -etrace= open ps
open( "/etc/ld.so.preload" , O_RDONLY|O_CLOEXEC) = 3
open( "/usr/lib/arm-linux-gnueabihf/libarmmem.so" , O_RDONLY|O_CLOEXEC) = 3
open( "/etc/ld.so.cache" , O_RDONLY|O_CLOEXEC) = 3
open( "/lib/arm-linux-gnueabihf/libprocps.so.3" , O_RDONLY|O_CLOEXEC) = 3
open( "/lib/arm-linux-gnueabihf/libdl.so.2" , O_RDONLY|O_CLOEXEC) = 3
open( "/lib/arm-linux-gnueabihf/libc.so.6" , O_RDONLY|O_CLOEXEC) = 3
open( "/sys/devices/system/cpu/online" , O_RDONLY|O_CLOEXEC) = 3
open( "/proc/self/stat" , O_RDONLY) = 3
open( "/proc/uptime" , O_RDONLY) = 3
open( "/proc/sys/kernel/pid_max" , O_RDONLY) = 4
open( "/proc/meminfo" , O_RDONLY) = 4
open( "/proc/1/stat" , O_RDONLY) = 6
open( "/proc/1/status" , O_RDONLY) = 6
open( "/proc/2/stat" , O_RDONLY) = 6
open( "/proc/2/status" , O_RDONLY) = 6
open( "/proc/3/stat" , O_RDONLY) = 6
open( "/proc/3/status" , O_RDONLY) = 6
...
Nifty, huh? strace is another one of those cool Linux utilites. It can be used to trace system calls (the essential interface between programs and the operating system) in a running program. More on that in a future post. :)