プロセスのオープンファイル数


fopenではnr=0で、files->fdt[fdt->max_fds]の空きインデックスを走査します。dup2システムコールではnrが任意のインデックスとなりファイルID取得を試みます。

ファイルIDがtsk->signal->rlim[RLIMIT_NOFILE].rlim_curを超えてれば取得できません。ファイルIDはfiles->fdt[fdt->max_fds]のインデックスで、nr > fdt->max_fdsdならfiles->fdt[]に空き領域がなく、でnr < sysctl_nr_openなら、expand_fdtable()で改めてfiles->fdt[]を拡張し取得します。
int expand_files(struct files_struct *files, int nr)
{
       struct fdtable *fdt;

       fdt = files_fdtable(files);

       if (nr >= rlimit(RLIMIT_NOFILE))
               return -EMFILE;

       if (nr < fdt->max_fds)
               return 0;

       if (nr >= sysctl_nr_open)
               return -EMFILE;

       return expand_fdtable(files, nr);
}

static inline unsigned long rlimit(unsigned int limit)
{
       return task_rlimit(current, limit);
}

static inline unsigned long task_rlimit(const struct task_struct *tsk,
               unsigned int limit)
{
       return ACCESS_ONCE(tsk->signal->rlim[limit].rlim_cur);
}
プロセスのデフォルトのRLIMIT_NOFILEのrlim_curは1024/rlim_maxは4096です。
[root@localhost ~]# cat /proc/self/limits
Limit                     Soft Limit           Hard Limit           Units
 :
Max open files            1024(rlim_cur)       4096(rlim_max)       files
:
/proc/self/limitsの .proc_readコールバックで、0からRLIM_NLIMITSのtask->signal->rlim[]のrlim_cur/rlim_maxを表示しますが、proc_writeコールバックは実装されません。
#define INF(NAME, MODE, read)                           \
       NOD(NAME, (S_IFREG|(MODE)),                     \
               NULL, &proc_info_file_operations,       \
               { .proc_read = read } )

static const struct pid_entry tgid_base_stuff[] = {
 :
       INF("limits",     S_IRUGO, proc_pid_limits),
 :
};

static int proc_pid_limits(struct task_struct *task, char *buffer)
{
       unsigned int i;
       int count = 0;
       unsigned long flags;
       char *bufptr = buffer;

       struct rlimit rlim[RLIM_NLIMITS];

       if (!lock_task_sighand(task, &flags))
               return 0;
       memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS);
       unlock_task_sighand(task, &flags);

       count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
                       "Limit", "Soft Limit", "Hard Limit", "Units");

       for (i = 0; i < RLIM_NLIMITS; i++) {
               if (rlim[i].rlim_cur == RLIM_INFINITY)
                       count += sprintf(&bufptr[count], "%-25s %-20s ",
                                        lnames[i].name, "unlimited");
               else
                       count += sprintf(&bufptr[count], "%-25s %-20lu ",
                                        lnames[i].name, rlim[i].rlim_cur);

               if (rlim[i].rlim_max == RLIM_INFINITY)
                       count += sprintf(&bufptr[count], "%-20s ", "unlimited");
               else
                       count += sprintf(&bufptr[count], "%-20lu ",
                                        rlim[i].rlim_max);

               if (lnames[i].unit)
                       count += sprintf(&bufptr[count], "%-10s\n",
                                        lnames[i].unit);
               else
                       count += sprintf(&bufptr[count], "\n");
       }

       return count;
}
sysctl_nr_openは、fdt->max_fds < nr < rlimit(RLIMIT_NOFILE)時での、files->fdt[]の拡張最大値。
[root@localhost ~]# cat /proc/sys/fs/nr_open
1048576

int sysctl_nr_open __read_mostly = 1024*1024;

static struct ctl_table fs_table[] = {
 :

       {
               .procname       = "nr_open",
               .data           = &sysctl_nr_open,
               .maxlen         = sizeof(int),
               .mode           = 0644,
               .proc_handler   = proc_dointvec_minmax,
               .extra1         = &sysctl_nr_open_min,
               .extra2         = &sysctl_nr_open_max,
       },
 :
}
rootプロセスの初期値はINIT_TASK(init_task)で、roott->signal->rlim[]はINIT_SIGNALS(init_signals)で、signal->rlim[RLIMIT_NOFILE] = { INR_OPEN_CUR, INR_OPEN_MAX },]となります。
void __init fork_init(unsigned long mempages)
{
#ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR
#ifndef ARCH_MIN_TASKALIGN
#define ARCH_MIN_TASKALIGN      L1_CACHE_BYTES
#endif
       task_struct_cachep =
               kmem_cache_create("task_struct", sizeof(struct task_struct),
                       ARCH_MIN_TASKALIGN, SLAB_PANIC | SLAB_NOTRACK, NULL);
#endif

       /* do the arch specific task caches init */
       arch_task_cache_init();

       max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);

       if (max_threads < 20)
               max_threads = 20;

       init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
       init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
       init_task.signal->rlim[RLIMIT_SIGPENDING] =
               init_task.signal->rlim[RLIMIT_NPROC];
}

struct task_struct init_task = INIT_TASK(init_task);

#define INIT_TASK(tsk)  \
{                                                                       \
       .state          = 0,                                            \
       .stack          = &init_thread_info,                            \
       .usage          = ATOMIC_INIT(2),                               \
       .flags          = PF_KTHREAD,                                   \
       .prio           = MAX_PRIO-20,                                  \
       .static_prio    = MAX_PRIO-20,                                  \
       .normal_prio    = MAX_PRIO-20,                                  \
       .policy         = SCHED_NORMAL,                                 \
       .cpus_allowed   = CPU_MASK_ALL,                                 \
       .mm             = NULL,                                         \
       .active_mm      = &init_mm,                                     \
       .se             = {                                             \
               .group_node     = LIST_HEAD_INIT(tsk.se.group_node),    \
       },                                                              \
       .rt             = {                                             \
               .run_list       = LIST_HEAD_INIT(tsk.rt.run_list),      \
               .time_slice     = HZ,                                   \
               .nr_cpus_allowed = NR_CPUS,                             \
       },                                                              \
       .tasks          = LIST_HEAD_INIT(tsk.tasks),                    \
       INIT_PUSHABLE_TASKS(tsk)                                        \
       .ptraced        = LIST_HEAD_INIT(tsk.ptraced),                  \
       .ptrace_entry   = LIST_HEAD_INIT(tsk.ptrace_entry),             \
       .real_parent    = &tsk,                                         \
       .parent         = &tsk,                                         \
       .children       = LIST_HEAD_INIT(tsk.children),                 \
       .sibling        = LIST_HEAD_INIT(tsk.sibling),                  \
       .group_leader   = &tsk,                                         \
       RCU_INIT_POINTER(.real_cred, &init_cred),                       \
       RCU_INIT_POINTER(.cred, &init_cred),                            \
       .comm           = INIT_TASK_COMM,                               \
       .thread         = INIT_THREAD,                                  \
       .fs             = &init_fs,                                     \
       .files          = &init_files,                                  \
       .signal         = &init_signals,                                \   <= オープンファイル
       .sighand        = &init_sighand,                                \
       .nsproxy        = &init_nsproxy,                                \
       .pending        = {                                             \
               .list = LIST_HEAD_INIT(tsk.pending.list),               \
               .signal = {{0}}},                                       \
       .blocked        = {{0}},                                        \
       .alloc_lock     = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),         \
       .journal_info   = NULL,                                         \
       .cpu_timers     = INIT_CPU_TIMERS(tsk.cpu_timers),              \
       .pi_lock        = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),        \
       .timer_slack_ns = 50000, /* 50 usec default slack */            \
       .pids = {                                                       \
               [PIDTYPE_PID]  = INIT_PID_LINK(PIDTYPE_PID),            \
               [PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID),           \
               [PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),            \
       },                                                              \
       .thread_group   = LIST_HEAD_INIT(tsk.thread_group),             \
       INIT_IDS                                                        \
       INIT_PERF_EVENTS(tsk)                                           \
       INIT_TRACE_IRQFLAGS                                             \
       INIT_LOCKDEP                                                    \
       INIT_FTRACE_GRAPH                                               \
       INIT_TRACE_RECURSION                                            \
       INIT_TASK_RCU_PREEMPT(tsk)                                      \
}

static struct signal_struct init_signals = INIT_SIGNALS(init_signals);

#define INIT_SIGNALS(sig) {                                             \
       .nr_threads     = 1,                                            \
       .wait_chldexit  = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
       .shared_pending = {                                             \
               .list = LIST_HEAD_INIT(sig.shared_pending.list),        \
               .signal =  {{0}}},                                      \
       .posix_timers    = LIST_HEAD_INIT(sig.posix_timers),            \
       .cpu_timers     = INIT_CPU_TIMERS(sig.cpu_timers),              \
       .rlim           = INIT_RLIMITS,                                 \   <= オープンファイル
       .cputimer       = {                                             \
               .cputime = INIT_CPUTIME,                                \
               .running = 0,                                           \
               .lock = __RAW_SPIN_LOCK_UNLOCKED(sig.cputimer.lock),    \
       },                                                              \
       .cred_guard_mutex =                                             \
                __MUTEX_INITIALIZER(sig.cred_guard_mutex),             \
       INIT_GROUP_RWSEM(sig)                                           \
}

#define INR_OPEN_CUR 1024       /* Initial setting for nfile rlimits */
#define INR_OPEN_MAX 4096       /* Hard limit for nfile rlimits */

#define INIT_RLIMITS                                                    \
{                                                                       \
       [RLIMIT_CPU]            = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_FSIZE]          = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_DATA]           = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_STACK]          = {       _STK_LIM,   _STK_LIM_MAX },   \
       [RLIMIT_CORE]           = {              0,  RLIM_INFINITY },   \
       [RLIMIT_RSS]            = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_NPROC]          = {              0,              0 },   \
       [RLIMIT_NOFILE]         = {   INR_OPEN_CUR,   INR_OPEN_MAX },   \   <= オープンファイル
       [RLIMIT_MEMLOCK]        = {    MLOCK_LIMIT,    MLOCK_LIMIT },   \
       [RLIMIT_AS]             = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_LOCKS]          = {  RLIM_INFINITY,  RLIM_INFINITY },   \
       [RLIMIT_SIGPENDING]     = {             0,             0 },     \
       [RLIMIT_MSGQUEUE]       = {   MQ_BYTES_MAX,   MQ_BYTES_MAX },   \
       [RLIMIT_NICE]           = { 0, 0 },                             \
       [RLIMIT_RTPRIO]         = { 0, 0 },                             \
       [RLIMIT_RTTIME]         = {  RLIM_INFINITY,  RLIM_INFINITY },   \
}
子プロセス作成時、copy_process()からcopy_signa()がコールされ、current->signal->rlimが新規プロセスに複写されます。
long do_fork(unsigned long clone_flags,
             unsigned long stack_start,
             struct pt_regs *regs,
             unsigned long stack_size,
             int __user *parent_tidptr,
             int __user *child_tidptr)
{
       struct task_struct *p;
       int trace = 0;
       long nr;

       if (clone_flags & CLONE_NEWUSER) {
               if (clone_flags & CLONE_THREAD)
                       return -EINVAL;

               if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
                               !capable(CAP_SETGID))
                       return -EPERM;
       }

       if (likely(user_mode(regs)) && !(clone_flags & CLONE_UNTRACED)) {
               if (clone_flags & CLONE_VFORK)
                       trace = PTRACE_EVENT_VFORK;
               else if ((clone_flags & CSIGNAL) != SIGCHLD)
                       trace = PTRACE_EVENT_CLONE;
               else
                       trace = PTRACE_EVENT_FORK;

               if (likely(!ptrace_event_enabled(current, trace)))
                       trace = 0;
       }

       p = copy_process(clone_flags, stack_start, regs, stack_size,
                        child_tidptr, NULL, trace);
       if (!IS_ERR(p)) {
               struct completion vfork;

               trace_sched_process_fork(current, p);

               nr = task_pid_vnr(p);

               if (clone_flags & CLONE_PARENT_SETTID)
                       put_user(nr, parent_tidptr);

               if (clone_flags & CLONE_VFORK) {
                       p->vfork_done = &vfork;
                       init_completion(&vfork);
                       get_task_struct(p);
               }

               wake_up_new_task(p);

               if (unlikely(trace))
                       ptrace_event(trace, nr);

               if (clone_flags & CLONE_VFORK) {
                       if (!wait_for_vfork_done(p, &vfork))
                               ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
               }
       } else {
               nr = PTR_ERR(p);
       }
       return nr;
}
tsk->signal->rlimにcurrent->signal->rlimを複写します。
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
{
       struct signal_struct *sig;

       if (clone_flags & CLONE_THREAD)
               return 0;

       sig = kmem_cache_zalloc(signal_cachep, GFP_KERNEL);
       tsk->signal = sig;
       if (!sig)
               return -ENOMEM;

       sig->nr_threads = 1;
       atomic_set(&sig->live, 1);
       atomic_set(&sig->sigcnt, 1);
       init_waitqueue_head(&sig->wait_chldexit);
       if (clone_flags & CLONE_NEWPID)
               sig->flags |= SIGNAL_UNKILLABLE;
       sig->curr_target = tsk;
       init_sigpending(&sig->shared_pending);
       INIT_LIST_HEAD(&sig->posix_timers);

       hrtimer_init(&sig->real_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
       sig->real_timer.function = it_real_fn;

       task_lock(current->group_leader);
       memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);  
       task_unlock(current->group_leader);

       posix_cpu_timers_init_group(sig);

       tty_audit_fork(sig);
       sched_autogroup_fork(sig);

#ifdef CONFIG_CGROUPS
       init_rwsem(&sig->group_rwsem);
#endif

       sig->oom_adj = current->signal->oom_adj;
       sig->oom_score_adj = current->signal->oom_score_adj;
       sig->oom_score_adj_min = current->signal->oom_score_adj_min;

       mutex_init(&sig->cred_guard_mutex);

       return 0;
}

補足

・CAP_SYS_RESOURCEケーパビリティを有してなければ、setrlimitシステムコールでのcurrent->signal->rlim[RLIMIT_NOFILE]の設定は、元のrlim_max以下でなければなりません。

・ファイル数制約は、プロセス毎におけるファイルID以外に、システム(CPU単位)としてファイルその物の制約も実装されています。


最終更新 2016/08/11 15:12:43 - north
(2015/10/31 14:44:34 作成)


検索

アクセス数
3676110
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。