/procのls一覧表示について
プロック擬似ファイルは実ファイルを持たないファイルシステムであり、このファイルを介してシステム情報およびプロセス情報をを設定確認できる機能を提供している。
本ファイルシステムは、proc_root_initでprocファイルシステム登録、マウント処理が行われ、マウント処理後、struct proc_dir_entry proc_rootをrootエントリーとして、proc_misc_init等でシステムに関するものはprocファイルシステム以下に静的にディレクトリが作成され、プロセスに関するものは動的に作成されるようになっている。
lsで/proc下のファイルを表示した場合、file_opretaionのproc_root_inode_operationsの.readdirのコールバック関数により/proc下のファイル一覧情報が取得される。このfile_opretaionはマウント時にスーパブロック取得において、rootのinodeに設定されるのinodeオペレーションにproc_rootの.proc_fopsをinode->i_fopに設定することで実現している。
mount時に呼ばれてスーパブロックを取得する。この時rootのinodeにproc_rootの時に.proc_iopsおよび.proc_fopsが設定される。
proc_readdirはproc_readdir_deを呼び出して、"."、".."および静的に作成した/procファイル以下のディレクトリー一覧が表示し、proc_pid_readdirでselfディレクトリーを表示して後、next_tgidでプロセスIDを1から順に、その時のスレッドグループIDを取得し、そのプロセスIDを表示するようになっている。next_tgidでPIDTYPE_PIDにつながれたプロセスIDから、グループリーダのプロセスIDを返す。それをプロセスIDを1から順に調べることで、システムの全グループリーダのプロセスIDを表示している。このことから一覧で表示されるプロセスIDは表示だけのもので、この段階ではその実態(inode等)は有していない。
補足
プロセスID以外はファイルシステム作成時、proc_misc_initがコールされ、proc_createでディレクトリ名と処理するコールバックを個々に設定することで作成している。
[root@localhost proc]# ls -f . mtrr vmstat loadavg 151 542 .. irq pagetypeinfo self 155 543 crypto cgroups buddyinfo 1 156 592 key-users sys vmallocinfo 2 157 914 keys bus slabinfo 3 159 1276 swaps tty interrupts 4 164 1277 latency_stats driver stat 5 167 1278 kallsyms fs partitions 6 217 1301 dma sysvipc cpuinfo 7 218 1384 timer_stats net devices 8 219 1393 timer_list sysrq-trigger locks 9 267 1404 iomem vmcore kmsg 10 268 1405 ioports kpageflags mounts 11 456 1483 sched_debug kpagecount execdomains 85 459 1521 mdstat kcore cmdline 86 494 1523 scsi schedstat filesystems 88 501 2112 misc modules version 89 504 acpi diskstats meminfo 91 537 fb zoneinfo uptime 92 541
本ファイルシステムは、proc_root_initでprocファイルシステム登録、マウント処理が行われ、マウント処理後、struct proc_dir_entry proc_rootをrootエントリーとして、proc_misc_init等でシステムに関するものはprocファイルシステム以下に静的にディレクトリが作成され、プロセスに関するものは動的に作成されるようになっている。
lsで/proc下のファイルを表示した場合、file_opretaionのproc_root_inode_operationsの.readdirのコールバック関数により/proc下のファイル一覧情報が取得される。このfile_opretaionはマウント時にスーパブロック取得において、rootのinodeに設定されるのinodeオペレーションにproc_rootの.proc_fopsをinode->i_fopに設定することで実現している。
struct proc_dir_entry proc_root = { .low_ino = PROC_ROOT_INO, .namelen = 5, .name = "/proc", .mode = S_IFDIR | S_IRUGO | S_IXUGO, .nlink = 2, .count = ATOMIC_INIT(1), .proc_iops = &proc_root_inode_operations, .proc_fops = &proc_root_operations, .parent = &proc_root, }; static const struct file_operations proc_root_operations = { .read = generic_read_dir, .readdir = proc_root_readdir, };
mount時に呼ばれてスーパブロックを取得する。この時rootのinodeにproc_rootの時に.proc_iopsおよび.proc_fopsが設定される。
int proc_fill_super(struct super_block *s) { ・・・・・ struct inode * root_inode;
s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; s->s_time_gran = 1; de_get(&proc_root); root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); if (!root_inode) goto out_no_root; root_inode->i_uid = 0; root_inode->i_gid = 0; s->s_root = d_alloc_root(root_inode); ・・・・・
struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, struct proc_dir_entry *de) { ・・・・・ if (de->proc_iops) ルートのIノードオペレーションにproc_root_inode_operationsを設定 inode->i_op = de->proc_iops; if (de->proc_fops) { ・・・・・ ルートのIノードファイルオペレーションにproc_root_operationsを設定 inode->i_fop = de->proc_fops; ・・・・・これでlsで/proc一覧表示させると、rootのinodeオペレーションとしてproc_root_operationsの.readdir = proc_root_readdirが呼ばれることになる。最初にproc_readdirで/proc下の静的に作成されているディレクトリーが表示させ、proc_pid_readdirで動的にプロセスIDを表示させる。
static int proc_root_readdir(struct file * filp, void * dirent, filldir_t filldir) { ・・・・・ if (nr < FIRST_PROCESS_ENTRY) { プロセスIDでないものを表示 int error = proc_readdir(filp, dirent, filldir); if (error <= 0) { unlock_kernel(); return error; } filp->f_pos = FIRST_PROCESS_ENTRY; } selfおよびプロセスIDを表示 ret = proc_pid_readdir(filp, dirent, filldir); return ret; }
proc_readdirはproc_readdir_deを呼び出して、"."、".."および静的に作成した/procファイル以下のディレクトリー一覧が表示し、proc_pid_readdirでselfディレクトリーを表示して後、next_tgidでプロセスIDを1から順に、その時のスレッドグループIDを取得し、そのプロセスIDを表示するようになっている。next_tgidでPIDTYPE_PIDにつながれたプロセスIDから、グループリーダのプロセスIDを返す。それをプロセスIDを1から順に調べることで、システムの全グループリーダのプロセスIDを表示している。このことから一覧で表示されるプロセスIDは表示だけのもので、この段階ではその実態(inode等)は有していない。
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { ・・・・・ ns = filp->f_dentry->d_sb->s_fs_info; iter.task = NULL; iter.tgid = filp->f_pos - TGID_OFFSET; for (iter = next_tgid(ns, iter); iter.task; iter.tgid += 1, iter = next_tgid(ns, iter)) { filp->f_pos = iter.tgid + TGID_OFFSET; if (proc_pid_fill_cache(filp, dirent, filldir, iter) < 0) { put_task_struct(iter.task); goto out; } } filp->f_pos = PID_MAX_LIMIT + TGID_OFFSET; ・・・・・ }
static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter) { ・・・・・
iter.task = NULL; pid = find_ge_pid(iter.tgid, ns); if (pid) { iter.tgid = pid_nr_ns(pid, ns); iter.task = pid_task(pid, PIDTYPE_PID); if (!iter.task || !has_group_leader_pid(iter.task)) { iter.tgid += 1; goto retry; } get_task_struct(iter.task); } rcu_read_unlock(); return iter; }
補足
プロセスID以外はファイルシステム作成時、proc_misc_initがコールされ、proc_createでディレクトリ名と処理するコールバックを個々に設定することで作成している。
void __init proc_misc_init(void) { static struct { char *name; int (*read_proc)(char*,char**,off_t,int,int*,void*); } *p, simple_ones[] = { {"loadavg", loadavg_read_proc}, {"uptime", uptime_read_proc}, {"meminfo", meminfo_read_proc}, {"version", version_read_proc}, ・・・・・ {"filesystems", filesystems_read_proc}, {"cmdline", cmdline_read_proc}, {"execdomains", execdomains_read_proc}, {NULL,} }; for (p = simple_ones; p->name; p++) create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL); proc_symlink("mounts", NULL, "self/mounts"); ・・・・・ proc_create("stat", 0, NULL, &proc_stat_operations); proc_create("interrupts", 0, NULL, &proc_interrupts_operations); proc_create("buddyinfo", S_IRUGO, NULL, &fragmentation_file_operations); proc_create("pagetypeinfo", S_IRUGO, NULL, &pagetypeinfo_file_ops); proc_create("vmstat", S_IRUGO, NULL, &proc_vmstat_file_operations); proc_create("zoneinfo", S_IRUGO, NULL, &proc_zoneinfo_file_operations); ・・・・・ }