/proc/sys/fs/file-nr
/proc/sys/fs/nr_openはプロセス毎の取得ファイルIDの最大値です。ファイルID取得は、0がら順に空きIDを走査して取得するので、結果的に最大取得数となります。ファイル管理は、このファイルIDをインデックスとするプロセスのfile[]にファイル構造体を設定する事で実装されます。
このファイル構造体取得の最大値はfiles_stat.maxで、/proc/sys/fs/file-nrで取得でき、/proc/sys/fs/file-maxで更新できます。なお、CAP_SYS_ADMINケーパビリチィならファイル取得での/proc/sys/fs/file-maxの制約はありません。
従って、バージョン3での/proc/sys/fs/nr_openは、s32=32の範囲で誤差を有する事になります。マルチCPUでなくとも、厳密な実装としてfiles_stat.nr_files + (2進数)*files_stat.countersとすべきで、運営上問題となるとは考えられませんが、実装上の不手際でないかと・・・。なお、ファイル取得時のチェックは、そのような実装となっています。
このファイル構造体取得の最大値はfiles_stat.maxで、/proc/sys/fs/file-nrで取得でき、/proc/sys/fs/file-maxで更新できます。なお、CAP_SYS_ADMINケーパビリチィならファイル取得での/proc/sys/fs/file-maxの制約はありません。
[root@localhost ~]# cat /proc/sys/fs/nr_open 1048576 [root@localhost ~]# cat /proc/sys/fs/file-nr 1056 0 49739
static struct ctl_table root_table[] = { { .procname = "kernel", .mode = 0555, .child = kern_table, }, { .procname = "vm", .mode = 0555, .child = vm_table, }, { .procname = "fs", .mode = 0555, .child = fs_table, }, { .procname = "debug", .mode = 0555, .child = debug_table, }, { .procname = "dev", .mode = 0555, .child = dev_table, }, { } }; static struct ctl_table fs_table[] = { : { .procname = "file-nr", .data = &files_stat, .maxlen = sizeof(files_stat), .mode = 0444, <- 4=100:読みのみ可 .proc_handler = proc_nr_files, }, { .procname = "file-max", .data = &files_stat.max_files, .maxlen = sizeof(files_stat.max_files), .mode = 0644, <- 6=110:読み書き可 .proc_handler = proc_doulongvec_minmax, }, : } int proc_nr_files(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { files_stat.nr_files = get_nr_files(); return proc_doulongvec_minmax(table, write, buffer, lenp, ppos); } static long get_nr_files(void) { return percpu_counter_read_positive(&nr_files); } static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc) { s64 ret = fbc->count; barrier(); if (ret >= 0) return ret; return 0; }files_stat.max_filesのデフォルトは8192ですが、files_init()で取得するキャッシュメモリに応じたファイル数となります。nr_files/nr_free_filesはバージョン2での実装で、バージョン3では使用されません。
#define NR_FILE 8192 struct files_stat_struct files_stat; struct files_stat_struct { unsigned long nr_files; unsigned long nr_free_files; unsigned long max_files; }; struct files_stat_struct files_stat = { .max_files = NR_FILE }; void __init files_init(unsigned long mempages) { unsigned long n; filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); n = (mempages * (PAGE_SIZE / 1024)) / 10; files_stat.max_files = max_t(unsigned long, n, NR_FILE); files_defer_init(); lg_lock_init(files_lglock); percpu_counter_init(&nr_files, 0); }
補足
struct percpu_counter { raw_spinlock_t lock; s64 count; #ifdef CONFIG_HOTPLUG_CPU struct list_head list; #endif s32 __percpu *counters; };バージョン2ではファイル取得毎に、files_stat.nr_files++とし、/proc/sys/fs/nr_openは、それを取得する事で実装されていましたが、バージョン3では、パフォーマンス向上を目的とし、static struct percpu_counter nr_filesの32までは*counterにビット進数で設定され、それを超えれば countに*countersのビット進数でのファイル数を設定し、*counters=0とし、以降のファイル取得は、ビット進数の*countersのインクリメント(シフト)で、上記繰り返しの実装です。また、ファイルクローズはcountがデクリメントされ、ファイル数が32未満の時、countは0で、countは-1(0xffffffff)となってしまいます。
従って、バージョン3での/proc/sys/fs/nr_openは、s32=32の範囲で誤差を有する事になります。マルチCPUでなくとも、厳密な実装としてfiles_stat.nr_files + (2進数)*files_stat.countersとすべきで、運営上問題となるとは考えられませんが、実装上の不手際でないかと・・・。なお、ファイル取得時のチェックは、そのような実装となっています。