これは Linux Advent Calendar 2019の 15 日目の記事です。procfs について勉強したことをまとめます。
検証環境
CentOS 8 を利用する。
]# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)
]# uname -a
Linux localhost.localdomain 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
procfs とは
疑似ファイルシステムのひとつ。 ディスク上に実体は存在せず、メモリから情報を取得する。
カーネルだけが知っている情報 (例えばシステム全体のロードアベレージ/CPU負荷/メモリ利用状況や、プロセスごとの情報)が取得できる。 専用のシステムコールを設けるのではなくファイルシステムにすることで、 システムコールの追加/変更をせずに新たな情報が追加できる。
基本的には読み込み専用のファイルが多いが、一部に書き込み可能なファイルも存在する。
ファイルシステム全般については前回調べた。
Linux ファイルシステム 徹底入門 - SIerだけど技術やりたいブログwww.kimullaa.com
代表的なファイル
Wikipedia や man proc が詳しい。
参考 Wikipedia procfs
参考 man page of proc
よく使いそうなものに内容を絞ってさらっと説明する。
システム全体
/proc/cpuinfo
cpu のコア数や CPU モデルなどの基礎情報が取れる。
]# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 60
model name : Intel(R) Core(TM) i5-4690 CPU @ 3.50GHz
stepping : 3
microcode : 0xffffffff
cpu MHz : 3499.998
cache size : 6144 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 4
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 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase bmi1 avx2 smep bmi2 erms invpcid xsaveopt flush_l1d arch_capabilities
...
/proc/meminfo
メモリに関する情報を表示できる。
]# cat /proc/meminfo
MemTotal: 7902220 kB
MemFree: 122836 kB
MemAvailable: 558992 kB
Buffers: 96 kB
Cached: 639408 kB
SwapCached: 456 kB
Active: 1020052 kB
Inactive: 694488 kB
Active(anon): 918604 kB
Inactive(anon): 160244 kB
Active(file): 101448 kB
Inactive(file): 534244 kB
Unevictable: 0 kB
...
/proc/cmdline
カーネルの起動引数を表示できる。
]# cat /proc/cmdline
BOOT_IMAGE=(hd0,gpt2)/vmlinuz-4.18.0-80.11.2.el8_0.x86_64 root=/dev/mapper/cl-root ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet
/proc/filesystems
サポートされているファイルシステム(カーネルに組み込まれているものかモジュールでロードされているもの)の一覧を表示できる。
]# cat /proc/filesystems
nodev sysfs
nodev rootfs
nodev ramfs
nodev bdev
nodev proc
nodev cpuset
nodev cgroup
nodev cgroup2
nodev tmpfs
nodev devtmpfs
nodev configfs
nodev debugfs
nodev tracefs
nodev securityfs
nodev sockfs
nodev dax
nodev bpf
nodev pipefs
nodev hugetlbfs
nodev devpts
nodev autofs
nodev pstore
nodev efivarfs
nodev mqueue
nodev selinuxfs
xfs
ext3
ext2
ext4
vfat
/proc/modules
カーネルにロードされているモジュールの一覧を表示できる。
]# cat /proc/modules
nf_tables_set 32768 5 - Live 0xffffffffc0676000
nft_fib_inet 16384 1 - Live 0xffffffffc0671000
nft_fib_ipv4 16384 1 nft_fib_inet, Live 0xffffffffc066c000
nft_fib_ipv6 16384 1 nft_fib_inet, Live 0xffffffffc0667000
nft_fib 16384 3 nft_fib_inet,nft_fib_ipv4,nft_fib_ipv6, Live 0xffffffffc0662000
nft_reject_inet 16384 4 - Live 0xffffffffc065d000
nf_reject_ipv4 16384 1 nft_reject_inet, Live 0xffffffffc0658000
nf_reject_ipv6 16384 1 nft_reject_inet, Live 0xffffffffc0653000
nft_reject 16384 1 nft_reject_inet, Live 0xffffffffc064e000
nft_ct 20480 7 - Live 0xffffffffc0648000
nft_chain_nat_ipv6 16384 6 - Live 0xffffffffc0643000
nf_conntrack_ipv6 20480 8 - Live 0xffffffffc0639000
nf_defrag_ipv6 20480 1 nf_conntrack_ipv6, Live 0xffffffffc062f000
...
/proc/mounts
現在マウントされているマウントポイントの一覧を表示できる。
]# cat /proc/mounts
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=3936592k,nr_inodes=984148,mode=755 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev 0 0
devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0
tmpfs /sys/fs/cgroup tmpfs ro,seclabel,nosuid,nodev,noexec,mode=755 0 0
cgroup /sys/fs/cgroup/systemd cgroup rw,seclabel,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd 0 0
pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0
efivarfs /sys/firmware/efi/efivars efivarfs rw,nosuid,nodev,noexec,relatime 0 0
bpf /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
...
/proc/uptime
システムの起動時間(1こめ)と idle プロセスが消費した時間の合計(2こめ)を表示できる。
]# cat /proc/uptime
2008.01 3391.90
/proc/loadavg
1,5,15 分のロードアベレージを表示できる。 2/168
は、現在スケジュールされているプロセスやスレッドの数/システム全体のプロセスやスレッドの数
。 一番最後は直近で生成されたプロセスのid。
]# cat /proc/loadavg
1.06 2.82 4.14 2/168 25833
プロセスごと
/proc/[pid]
ではプロセスごとの情報が取れる。/tmp
ディレクトリで sleep を実行しておき、このプロセスの情報を確認する。
]# cd /tmp
]# sleep 1000 &
[1] 4048
/proc/[pid]/comm
プロセス実行時のコマンドを表示する。
]# cat /proc/4048/comm
sleep
/proc/[pid]/cmdline
プロセス実行時のコマンドと引数を表示する。
# cat /proc/4048/cmdline
sleep1000
/proc/[pid]/cwd
プロセスのワーキングディレクトリのシンボリックリンク。
]# ls -ald /proc/4048/cwd
lrwxrwxrwx. 1 root root 0 12月 14 08:06 /proc/4048/cwd -> /tmp
/proc/[pid]/environ
プロセスの環境変数を表示する。
]# cat /proc/4048/environ
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.m4a=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.oga=01;36:*.opus=01;36:*.spx=01;36:*.xspf=01;36:SSH_CONNECTION=192.168.11.104 57035 192.168.11.111 22MODULES_RUN_QUARANTINE=LD_LIBRARY_PATHLANG=ja_JP.UTF-8HISTCONTROL=ignoredupsHOSTNAME=localhost.localdomainXDG_SESSION_ID=4MODULES_CMD=/usr/share/Modules/libexec/modulecmd.tclUSER=rootENV=/usr/share/Modules/init/profile.shSELINUX_ROLE_REQUESTED=PWD=/tmpHOME=/rootSSH_CLIENT=192.168.11.104 57035 22SELINUX_LEVEL_REQUESTED=BASH_ENV=/usr/share/Modules/init/bashLOADEDMODULES=SSH_TTY=/dev/pts/1MAIL=/var/spool/mail/rootTERM=xtermSHELL=/bin/bashSELINUX_USE_CURRENT_RANGE=SHLVL=1MODULEPATH=/usr/share/Modules/modulefiles:/etc/modulefiles:/usr/share/modulefilesLOGNAME=rootDBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/busXDG_RUNTIME_DIR=/run/user/0MODULEPATH_modshare=/usr/share/modulefiles:1:/etc/modulefiles:1:/usr/share/Modules/modulefiles:1PATH=/usr/share/Modules/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/binMODULESHOME=/usr/share/ModulesHISTSIZE=1000LESSOPEN=||/usr/bin/lesspipe.sh %sBASH_FUNC_module%%=() { _moduleraw "$@" 2>&1
}BASH_FUNC_switchml%%=() { typeset swfound=1;
if [ "${MODULES_USE_COMPAT_VERSION:-0}" = '1' ]; then
typeset swname='main';
if [ -e /usr/share/Modules/libexec/modulecmd.tcl ]; then
typeset swfound=0;
unset MODULES_USE_COMPAT_VERSION;
fi;
else
typeset swname='compatibility';
...
/proc/[pid]/exe
プロセスを起動したコマンドのフルパスを表示する。
]# ls -ald /proc/4048/exe
lrwxrwxrwx. 1 root root 0 12月 14 08:06 /proc/4048/exe -> /usr/bin/sleep
/proc/[pid]/fd/
プロセスがオープンしているファイルを表示する。 0(stdin), 1(stdout), 2(stderr)。
]# ls -al /proc/4048/fd
合計 0
dr-x------. 2 root root 0 12月 14 08:06 .
dr-xr-xr-x. 9 root root 0 12月 14 08:05 ..
lrwx------. 1 root root 64 12月 14 08:09 0 -> /dev/pts/1
lrwx------. 1 root root 64 12月 14 08:09 1 -> /dev/pts/1
lrwx------. 1 root root 64 12月 14 08:09 2 -> /dev/pts/1
/proc/[pid]/maps/
プロセス空間のメモリを表示する。
# cat /proc/4048/maps
55643566b000-556435672000 r-xp 00000000 fd:00 100669977 /usr/bin/sleep
556435872000-556435873000 r--p 00007000 fd:00 100669977 /usr/bin/sleep
556435873000-556435874000 rw-p 00008000 fd:00 100669977 /usr/bin/sleep
556436829000-55643684a000 rw-p 00000000 00:00 0 [heap]
7fa44d2e7000-7fa44d4a1000 r-xp 00000000 fd:00 1789 /usr/lib64/libc-2.28.so
7fa44d4a1000-7fa44d6a1000 ---p 001ba000 fd:00 1789 /usr/lib64/libc-2.28.so
7fa44d6a1000-7fa44d6a5000 r--p 001ba000 fd:00 1789 /usr/lib64/libc-2.28.so
7fa44d6a5000-7fa44d6a7000 rw-p 001be000 fd:00 1789 /usr/lib64/libc-2.28.so
7fa44d6a7000-7fa44d6ab000 rw-p 00000000 00:00 0
7fa44d6ab000-7fa44d6d3000 r-xp 00000000 fd:00 1782 /usr/lib64/ld-2.28.so
...
/proc/[pid]/root
プロセスのルートディレクトリを表示する。chroot したときは別の場所を指す。(dockerとか)
]# ls /proc/4048/root/
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
試しにコンテナを立ち上げてみる。(CentOS 8 からは podman が使えます)
// コンテナを立ち上げてファイルを作る
]# podman run -it centos bash
Trying to pull registry.redhat.io/centos:latest...Failed
Trying to pull quay.io/centos:latest...Failed
Trying to pull docker.io/centos:latest...Getting image source signatures
Copying blob 729ec3a6ada3: 67.50 MiB / 68.21 MiB [=============================]
Copying blob 729ec3a6ada3: 68.21 MiB / 68.21 MiB [==========================] 8s
Copying config 0f3e07c0138f: 2.13 KiB / 2.13 KiB [==========================] 0s
Writing manifest to image destination
Storing signatures
[root@13f3333aff33 /]# touch hello_from_podman
// 別ターミナル
// ホストのルートには反映されない
]# ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
// ホストでコンテナの pid を確認して ls するとコンテナ内のルートが見える
]# ls /proc/6978/root/
bin dev etc hello_from_podman home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
/proc/[pid]/stat
色々取れる。CPU 割り当て時間やメモリ使用率など。
]# cat /proc/4048/stat
4048 (sleep) S 31244 4048 31244 34817 25721 1077952512 135 0 0 0 0 0 0 0 20 0 1 0 624150 5591040 73 18446744073709551615 93888881012736 93888881040864 140722657125040 0 0 0 0 0 0 1 0 0 17 2 0 0 0 0 0 93888883141456 93888883142784 93888899616768 140722657132307 140722657132318 140722657132318 140722657136617 0
/proc/[pid]/status
プロセスの状態が取れる。 例えばタスクが実行中か寝てるか、親プロセスidは何か、など。
]# cat /proc/4048/status | head
Name: sleep
Umask: 0022
State: S (sleeping)
Tgid: 4048
Ngid: 0
Pid: 4048
PPid: 31244
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
ユーザ空間ライブラリでの利用
メモリ使用率を題材に、ユーザ空間ライブラリがどのように /proc
を利用しているか確認する。メモリについての基本的な情報は次を参考にしてください。
Linux メモリ管理 徹底入門(プロセス編) - SIerだけど技術やりたいブログwww.kimullaa.com
今回調べる対象は free。 他の人もよく題材にしてるようだが、自分の勉強のためなので関係なく進める。
【RHEL】linuxメモリのfreeとmeminfoの関係を図解し利用率の計算方法を説明してみる
参考 free(1)のtotalとかusedなどの各項目をカーネルの方から見てみる
参考 /proc/meminfo の Cached
]# free
total used free shared buff/cache available
Mem: 7902032 7023984 544584 8524 333464 624448
Swap: 6713340 11276 6702064
ソースコードをダウンロードし、コードを読み進める。
参考 Linux rpmパッケージのコードリーディングをしたい
すると free の出力部分は次のようになっていた。
int main(int argc, char **argv)
{
...
do {
meminfo();
/* Translation Hint: You can use 9 character words in
* the header, and the words need to be right align to
* beginning of a number. */
if (flags & FREE_WIDE) {
printf(_(" total used free shared buffers cache available"));
} else {
printf(_(" total used free shared buff/cache available"));
}
printf("\n");
printf("%-7s", _("Mem:"));
printf(" %11s", scale_size(kb_main_total, flags, args));
printf(" %11s", scale_size(kb_main_used, flags, args));
printf(" %11s", scale_size(kb_main_free, flags, args));
printf(" %11s", scale_size(kb_main_shared, flags, args));
if (flags & FREE_WIDE) {
printf(" %11s", scale_size(kb_main_buffers, flags, args));
printf(" %11s", scale_size(kb_main_cached, flags, args));
} else {
printf(" %11s", scale_size(kb_main_buffers+kb_main_cached, flags, args));
}
printf(" %11s", scale_size(kb_main_available, flags, args));
では kb_main_total などの変数がどこで定義されているかというと、 次の部分で /proc/meminfo
を読み込んでいた。
#define MEMINFO_FILE "/proc/meminfo"
static int meminfo_fd = -1;
...
void meminfo(void){
...
static const mem_table_struct mem_table[] = {
{"Active", &kb_active}, // important
{"Active(file)", &kb_active_file},
{"AnonPages", &kb_anon_pages},
{"Bounce", &kb_bounce},
{"Buffers", &kb_main_buffers}, // important
{"Cached", &kb_page_cache}, // important
{"CommitLimit", &kb_commit_limit},
{"Committed_AS", &kb_committed_as},
{"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty
{"HighFree", &kb_high_free},
{"HighTotal", &kb_high_total},
{"Inact_clean", &kb_inact_clean},
{"Inact_dirty", &kb_inact_dirty},
{"Inact_laundry",&kb_inact_laundry},
{"Inact_target", &kb_inact_target},
{"Inactive", &kb_inactive}, // important
{"Inactive(file)",&kb_inactive_file},
{"LowFree", &kb_low_free},
{"LowTotal", &kb_low_total},
{"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped
{"MemAvailable", &kb_main_available}, // important
{"MemFree", &kb_main_free}, // important
{"MemTotal", &kb_main_total}, // important
{"NFS_Unstable", &kb_nfs_unstable},
{"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages
{"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages
{"SReclaimable", &kb_slab_reclaimable}, // "slab reclaimable" (dentry and inode structures)
{"SUnreclaim", &kb_slab_unreclaimable},
{"Shmem", &kb_main_shared}, // kernel 2.6.32 and later
{"Slab", &kb_slab}, // kB version of vmstat nr_slab
{"SwapCached", &kb_swap_cached},
{"SwapFree", &kb_swap_free}, // important
{"SwapTotal", &kb_swap_total}, // important
{"VmallocChunk", &kb_vmalloc_chunk},
{"VmallocTotal", &kb_vmalloc_total},
{"VmallocUsed", &kb_vmalloc_used},
{"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback
};
...
// /proc/meminfo をバッファに読み込み、 mem_table の key と一致する項目に値を設定する
FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
...
head = buf;
for(;;){
tail = strchr(head, ':');
if(!tail) break;
*tail = '\0';
if(strlen(head) >= sizeof(namebuf)){
head = tail+1;
goto nextline;
}
strcpy(namebuf,head);
found = bsearch(&findme, mem_table, mem_table_count,
sizeof(mem_table_struct), compare_mem_table_structs
);
head = tail+1;
if(!found) goto nextline;
*(found->slot) = (unsigned long)strtoull(head,&tail,10);
nextline:
tail = strchr(head, '\n');
if(!tail) break;
head = tail+1;
}
...
また一部の変数(kb_main_used など)は /proc/meminfo
に存在しないが、 そういう項目は計算して定義していた。
...
mem_used = kb_main_total - kb_main_free - kb_main_cached - kb_main_buffers;
if (mem_used < 0)
mem_used = kb_main_total - kb_main_free;
kb_main_used = (unsigned long)mem_used;
まあソースコードなんて見なくても man に書いてるんですけどね…
total Total installed memory (MemTotal and SwapTotal in /proc/meminfo)
used Used memory (calculated as total - free - buffers - cache)
free Unused memory (MemFree and SwapFree in /proc/meminfo)
shared Memory used (mostly) by tmpfs (Shmem in /proc/meminfo)
buffers
Memory used by kernel buffers (Buffers in /proc/meminfo)
cache Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
buff/cache
Sum of buffers and cache
available
Estimation of how much memory is available for starting new applications, without swapping. Unlike the data provided by the cache or free
fields, this field takes into account page cache and also that not all reclaimable memory slabs will be reclaimed due to items being in use
(MemAvailable in /proc/meminfo, available on kernels 3.14, emulated on kernels 2.6.27+, otherwise the same as free)
vmstat の cpu 使用率やロードアベレージなどについても、 上記と似た流れで /proc 配下のファイルを読み込んでデータを適宜加工して表示している。 これについてはソースコードまでは説明せず、 strace で ファイルが open されていることで代用する。
]# strace vmstat 2>&1 | grep "/proc/stat"
openat(AT_FDCWD, "/proc/stat", O_RDONLY) = 4
]# strace uptime 2>&1 | grep "/proc/uptime"
openat(AT_FDCWD, "/proc/uptime", O_RDONLY) = 3
ということで、リソース情報を表示するユーザ空間のライブラリは、 大体が /proc
を利用していることが分かった。 つまり /proc
とその使われ方を理解すれば、各種コマンドの意味や妥当性を判断できるようになる。
procfs を見てみよう
そうと分かれば procfs を読んでみたくなるのが心情。 先ほどと同様の方法でカーネルソースをダウンロードする。
参考 Linux rpmパッケージのコードリーディングをしたい
procfs はfs/proc/
にある。
]# ls fs/proc
Kconfig base.c cpuinfo.c fd.h internal.h kmsg.c namespaces.c proc_net.c root.c stat.c thread_self.c version.c
Makefile cmdline.c devices.c generic.c interrupts.c loadavg.c nommu.c proc_sysctl.c self.c task_mmu.c uptime.c vmcore.c
array.c consoles.c fd.c inode.c kcore.c meminfo.c page.c proc_tty.c softirqs.c task_nommu.c util.c
実装は他のファイルシステムと同様だが、簡単に作成できるように、 大部分の処理がfs/proc/generic.c
にまとめられているみたい。(f_ops の設定とかもろもろ)
最終的には open 時に次の処理が実行される。
static int meminfo_proc_show(struct seq_file *m, void *v)
{
struct sysinfo i;
unsigned long committed;
long cached;
long available;
unsigned long pages[NR_LRU_LISTS];
int lru;
si_meminfo(&i);
si_swapinfo(&i);
committed = percpu_counter_read_positive(&vm_committed_as);
cached = global_node_page_state(NR_FILE_PAGES) -
total_swapcache_pages() - i.bufferram;
if (cached < 0)
cached = 0;
for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
pages[lru] = global_node_page_state(NR_LRU_BASE + lru);
available = si_mem_available();
show_val_kb(m, "MemTotal: ", i.totalram);
show_val_kb(m, "MemFree: ", i.freeram);
show_val_kb(m, "MemAvailable: ", available);
show_val_kb(m, "Buffers: ", i.bufferram);
show_val_kb(m, "Cached: ", cached);
show_val_kb(m, "SwapCached: ", total_swapcache_pages());
show_val_kb(m, "Active: ", pages[LRU_ACTIVE_ANON] +
pages[LRU_ACTIVE_FILE]);
show_val_kb(m, "Inactive: ", pages[LRU_INACTIVE_ANON] +
...
これを読んでいくと、ページキャッシュ(cached
) は file-backed なページ(global_node_page_state(NR_FILE_PAGES)
) から スワップしてるページ(total_swapcache_pages()
anon のページはスワップ時にファイルに書き出すので、そのタイミングで NR_FILE_PAGES に変わるため) とブロックデバイス向けのキャッシュ(i.bufferram
バッファキャッシュは実装上ブロックデバイスに対するページキャッシュになってるため) を引いたものだとわかる。…わかるのか…?
まあソースコードなんて見なくても proc の man に書いてるんですけどね…
/proc/meminfo
This file reports statistics about memory usage on the system. It is used by free(1) to report the amount of free and used memory (both
physical and swap) on the system as well as the shared memory and buffers used by the kernel. Each line of the file consists of a parame‐
ter name, followed by a colon, the value of the parameter, and an option unit of measurement (e.g., "kB"). The list below describes the
parameter names and the format specifier required to read the field value. Except as noted below, all of the fields have been present
since at least Linux 2.6.0. Some fields are displayed only if the kernel was configured with various options; those dependencies are noted
in the list.
MemTotal %lu
Total usable RAM (i.e., physical RAM minus a few reserved bits and the kernel binary code).
MemFree %lu
The sum of LowFree+HighFree.
MemAvailable %lu (since Linux 3.14)
An estimate of how much memory is available for starting new applications, without swapping.
Buffers %lu
Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
Cached %lu
In-memory cache for files read from the disk (the page cache). Doesn't include SwapCached.
仮想ファイルを作ってみよう
procfs の実装も追えたので、そうなれば、自分で実装してみたくなるのが心情。 次のサイトを参考に独自にカーネルをビルドし、仮想ファイルを作成する。
CentOS 7: カーネルを再ビルドする
meminfo.c
を参考にして
fs_initcall();
で初期化用関数を呼び、- その中で
proc_dir_entry
を登録する関数proc_create_single
(generic.c にある)を呼び出す。 - このときの引数に渡したハンドラが open 時に実行される。
実際に作成したパッチがこちら。
]# diff -u ~/rpmbuild/SPECS/kernel.spec.org ~/rpmbuild/SPECS/kernel.spec
--- /root/rpmbuild/SPECS/kernel.spec.org 2019-12-14 02:10:44.617212554 -0500
+++ /root/rpmbuild/SPECS/kernel.spec 2019-12-14 10:43:13.286154014 -0500
@@ -30,13 +30,13 @@
%global zipsed -e 's/\.ko$/\.ko.xz/'
%endif
-# define buildid .local
+%define buildid .local
%define rpmversion 4.18.0
%define pkgrelease 80.11.2.el8_0
# allow pkg_release to have configurable %%{?dist} tag
-%define specrelease 80.11.2%{?dist}
+%define specrelease 80.11.9999%{?dist}
%define pkg_release %{specrelease}%{?buildid}
@@ -418,6 +418,8 @@
Patch1001: debrand-rh_taint.patch
Patch1002: debrand-rh-i686-cpu.patch
+Patch99999: custom.patch
+
# empty final patch to facilitate testing of kernel patches
Patch999999: linux-kernel-test.patch
@@ -880,6 +882,7 @@
ApplyOptionalPatch debrand-single-cpu.patch
ApplyOptionalPatch debrand-rh_taint.patch
ApplyOptionalPatch debrand-rh-i686-cpu.patch
+ApplyOptionalPatch custom.patch
# END OF PATCH APPLICATIONS
]# cat ~/rpmbuild/SOURCES/custom.patch
diff -uprN fs/proc.org/Makefile fs/proc/Makefile
--- a/fs/proc/Makefile 2019-12-14 10:39:54.596541412 -0500
+++ b/fs/proc/Makefile 2019-12-14 10:40:23.841042831 -0500
@@ -19,6 +19,7 @@ proc-y += devices.o
proc-y += interrupts.o
proc-y += loadavg.o
proc-y += meminfo.o
+proc-y += custom.o
proc-y += stat.o
proc-y += uptime.o
proc-y += util.o
diff -uprN fs/proc.org/custom.c fs/proc/custom.c
--- a/fs/proc/custom.c 1969-12-31 19:00:00.000000000 -0500
+++ b/fs/proc/custom.c 2019-12-14 10:38:06.816378923 -0500
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/quicklist.h>
+#include <linux/seq_file.h>
+#include <linux/atomic.h>
+#include "internal.h"
+
+static int greeting(struct seq_file *m, void *v)
+{
+ seq_write(m, "ho-ho-ho\n", 9);
+ return 0;
+}
+static int __init proc_custom_init(void)
+{
+ proc_create_single("christmas", 0, NULL, greeting);
+ return 0;
+}
+
+fs_initcall(proc_custom_init);
実行結果はこちら。あ、もしかしてあなたは…
]# uname -r
4.18.0-80.11.9999.el8.local.x86_64
]# cat /proc/christmas
ho-ho-ho
サンタさーん!
それでは、よいクリスマスを。