Одна из ключевых особенность Unix-систем заключается в работе со всем программно-аппаратным комплексом через файлы(например файловая система devfs для работы с устройствами, которая монтируется в каталог /dev), тот же механизм применен для работы с процессами через файловую систему procfs и именно он нем пойдет речь в данной статье.

Автор: unix-admin.su

Работаем с procfs в Linux - процессы и переменные ядра.

procfs - Виртуальная файловая система в UNIX-like системах. Предоставляет информацию о процессах из ядра. Кроме того в Linux-системе реализовано получение информации о некоторых переменных ядра и изменении некоторых из них. Впервые procfs появилась в далеком 1965 году в релизе 8-ой редакции UNIX и предоставляла интерфейс для управления процессами. С тех пор прошло много времени и изначальная реализация сохранилась лишь в потомках UNIX, таких как Solaris, *BSD, Plan9 и др.

1. Структура

Не будем долго ходить вокруг да около, давайте заглянем в каталог /proc:

[root@unix-admin /]# ls /proc/
1      17     20587  23993  305  326  357  5    65   9010  9395       cmdline      fb           keys        misc          self           timer_stats
10     18     20614  23994  306  328  42   527  7    9012  9402       consoles     filesystems  key-users   modules       slabinfo       tty
11     19     20677  24161  31   33   43   528  8    9013  9403       cpuinfo      fs           kmsg        mounts        softirqs       uptime
11660  2      20681  24163  310  330  44   532  803  9014  9404       crypto       interrupts   kpagecount  mtrr          stat           version
12     20     20682  29     32   331  447  534  807  9015  98         devices      iomem        kpageflags  net           swaps          vmallocinfo
13     20562  20683  29768  321  338  45   538  825  9016  acpi       diskstats    ioports      loadavg     pagetypeinfo  sys            vmstat
14     20564  207    29769  322  34   458  548  833  9058  buddyinfo  dma          irq          locks       partitions    sysrq-trigger  zoneinfo
15     20565  21     3      323  355  481  583  874  9339  bus        driver       kallsyms     mdstat      sched_debug   sysvipc
16     20586  23991  30     324  356  489  6    9    9380  cgroups    execdomains  kcore        meminfo     scsi          timer_list

Мы видим множество каталогов и некоторое количество файлов с говорящими именами. Каталоги с именами-числами ничто что иное как PID процессов запущенных в данный момент в системе. Остальные файлы и каталоги с «человеческими именами» это информация о переменных ядра, например выполнив команду cat /proc/cpuinfo мы увидим информацию о процессорах установленных в компьютере

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 42
model name      : Intel(R) Xeon(R) CPU E5-26xx (Sandy Bridge)
stepping        : 1
microcode       : 0x1
cpu MHz         : 2099.998
cache size      : 4096 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx hypervisor lahf_lm arat xsaveopt
bogomips        : 4199.99
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management: 

а если выполним команду cat /proc/meminfo

MemTotal:        1016784 kB
MemFree:           84564 kB
MemAvailable:     161876 kB
Buffers:           66824 kB
Cached:           160292 kB
SwapCached:            0 kB
Active:           729284 kB
Inactive:         119796 kB
Active(anon):     644084 kB
Inactive(anon):    35608 kB
Active(file):      85200 kB
Inactive(file):    84188 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:        621964 kB
Mapped:            30016 kB
Shmem:             57728 kB
Slab:              54736 kB
SReclaimable:      41096 kB
SUnreclaim:        13640 kB
KernelStack:        2032 kB
PageTables:        11296 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      508392 kB
Committed_AS:    1428728 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       23536 kB
VmallocChunk:   34359708156 kB
HardwareCorrupted:     0 kB
AnonHugePages:    223232 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       71552 kB
DirectMap2M:      976896 kB

Увидим информацию о памяти(в том числе установленной, свободной, используемой, буферах и т.п) В каталоге /proc/sys/ содержатся переменные ядра, которые мы устанавливаем через утилиту sysctl, но об этом мы поговорим позже, давайте сначала разберемся с основной функцией procfs - работе с процессами.

2. Работа с процессами

Для предоставления данных о процессах, procfs используют такие программы как ps, w, top, для более наглядного представления давайте напишем на bash скрипт, который будет показывать нам некоторую информацию, подобно тому, как это делает ps. Давайте выберем какой-нибудь процесс в системе и посмотрим что скажет утилита ps. Мой выбор пал на процесс mysqld, его pid 3057.

[root@unix-admin ~]# ps aux |grep 3057

mysql     3057  0.0  8.4 1001388 86396 ?       Sl   16:59   0:01 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock --port=3306

Мы имеем следующее:
USER: имя пользователя запустившего процесса: mysql
PID: Pid процесса
CPU: процент расходования процессорного времени на момент выполнения команды :0.0%
MEM: Общий расход памяти в процентах: 10.7%
VSZ: Virtual Set siZe - 1171608kB = 1144.14Mb = 1.144Gb
RSS: Resident Set Size - 109812kB = 107.23Mb
TTY: Номер терминала, с которого выполняется команда, в данном случае = ?, т.к. он не привязан ни к одному терминалу.
START: Sep07
TIME: время работы процесса 9:39 в часах
COMMAND: полная команда запуска: /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock --port=3306
Данные которые нужно собрать нам ясны, посмотри что предлагает procfs. Для этого обратимся к файлу cat /proc/2776/status, заметьте, что 9339 это PID процесса, информацию о котором мы хотим узнать:

[root@unix-admin ~]# cat /proc/3057/status
Name:   mysqld_safe
State:  S (sleeping)
Tgid:   2776
Ngid:   0
Pid:    2776
PPid:   1
TracerPid:      0
Uid:    27      27      27      27
Gid:    27      27      27      27
FDSize: 256
Groups: 27
VmPeak:   113124 kB
VmSize:   113124 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:      1584 kB
VmRSS:      1584 kB
VmData:      208 kB
VmStk:       136 kB
VmExe:       884 kB
VmLib:      2048 kB
VmPTE:        48 kB
VmSwap:        0 kB
Threads:        1
SigQ:   0/3799
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000010000
SigIgn: 0000000000005007
SigCgt: 0000000000010000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000001fffffffff
Seccomp:        0
Cpus_allowed:   1
Cpus_allowed_list:      0
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:        178
nonvoluntary_ctxt_switches:     0

из этого мы можем вытащить данные о User, PID, State, VSZ, RSS, т.е большинство, однако остается еще несколько, в /proc/3057/cmdline содержится COMMAND. Для того, чтобы получить tty, нам нужно посмотреть куда ссылается файл /proc/3057/fd/0, если на /dev/null, значит записать «?», если на /dev/tty*, значит tty и номер за ним.

TTY=$(readlink /proc/3057/fd/0)
if [[ $TTY  == "/dev/null" ]]
then TTY=\?
fi

Следующий пункт время и дата старта программы, согласно man proc, данные содержатся в /proc/3057/stat, в 22 колонке и представляет из себя количество секунд прошедшее от старта системы до запуска программы . Вычислять это значение будем следующим образом:

1. Возьмем uptime системы - время прошедшее с последней загрузки системы. Для упрощения задачи, возьмем время создания файловой системы proc, она создается именно в тот момент, откуда исчисляется uptime

[root@unix-admin ~]# stat -c %Z /proc/
1496670237

2. Прибавляем к нему значение колонки 22 из файла /proc/3057/stat

[root@unix-admin ~]# cat /proc/3057/stat |awk '{print $22}'
91729

3. Затем конвертируем в человеческое представление с помощью команды date. Тут надо сделать небольшое отступление, и сказать, что данные в /proc/3057/stat содержатся в «clock ticks»(sysconf(_SC_CLK_TCK)), для того чтобы вычислить секунды, нам нужно узнать количество тиков в секунде, для это воспользуемся командой getconf CLK_TCK.

[root@unix-admin ~]# getconf CLK_TCK
100
[root@unix-admin ~]# date -d @$(( 1496670237+(91729/100) )) +"%d.%m.%y(%H:%m:%S)"
05.06.17(16:06:14)

Кроме того, оригинал программы дату и время отображает в другом формате и зависит он от того, какое количество времени проработала программа, кроме того колонка TIME отображает не время в которое был запущен процесс, а количество минут и/или часов которое он проработал, для упрощения нашей задачи вы просто напишем дату и время старта программы. Теперь мы можем написать наш скрипт, некоторе подобие утилиты ps с ключами -aux. Продолжение в Части2.