ファイルディスクリプタ


struct fileはstruct fdtableで管理され、current->files->fdtab->fd[]に、配列サイズがfdtab->max_fdsを最大として設定されます。そのインデックスがファイルディスクリプタとなります。close_on_exec/open_fdsは、fd[]のファイルがFD_CLOEXECのファイルかどうかをビットインデックスで管理します。 
struct fdtable {
       unsigned int max_fds;
       struct file __rcu **fd; 
       fd_set *close_on_exec;
       fd_set *open_fds;
       struct rcu_head rcu;
       struct fdtable *next;
};

current->files->fdtab->fd[];
ファイルopen時、do_sys_open()でget_unused_fd_flags()で取得し、do_filp_open()で取得したstruct fileを、fd_install()でバインドします。
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
       struct open_flags op;
       int lookup = build_open_flags(flags, mode, &op);
       char *tmp = getname(filename);
       int fd = PTR_ERR(tmp);

       if (!IS_ERR(tmp)) {
               fd = get_unused_fd_flags(flags);
               if (fd >= 0) {
                       struct file *f = do_filp_open(dfd, tmp, &op, lookup);
                       if (IS_ERR(f)) {
                               put_unused_fd(fd);
                               fd = PTR_ERR(f);
                       } else {
                               fsnotify_open(f);
                               fd_install(fd, f);
                       }
               }
               putname(tmp);
       }
       return fd;
}
get_unused_fd_flags()はalloc_fd()を、検索開始ファイルIDをstart=0とし走査取得します。files->next_fdは前回取得したIDで、files->next_fd-fd区間は、取得済みの可能性があります。find_next_zero_bit()で、fd以降の空きファイルIDを取得します。

FD_SET(fd, fdt->open_fds)、flagがO_CLOEXECならFD_SET(fd, fdt->close_on_exec)でfd位置のビットをセットします。
#define get_unused_fd_flags(flags) alloc_fd(0, (flags))

int alloc_fd(unsigned start, unsigned flags)
{
       struct files_struct *files = current->files;
       unsigned int fd;
       int error;
       struct fdtable *fdt;

       spin_lock(&files->file_lock);
repeat:
       fdt = files_fdtable(files);
       fd = start;
       if (fd < files->next_fd)
               fd = files->next_fd;

       if (fd < fdt->max_fds)
               fd = find_next_zero_bit(fdt->open_fds->fds_bits,
                                          fdt->max_fds, fd);

       error = expand_files(files, fd);
       if (error < 0)
               goto out;

       if (error)
               goto repeat;

       if (start <= files->next_fd)
               files->next_fd = fd + 1;

       FD_SET(fd, fdt->open_fds);
       if (flags & O_CLOEXEC)
               FD_SET(fd, fdt->close_on_exec);
       else
               FD_CLR(fd, fdt->close_on_exec);
       error = fd;
#if 1
       /* Sanity check */
       if (rcu_dereference_raw(fdt->fd[fd]) != NULL) {
               printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
               rcu_assign_pointer(fdt->fd[fd], NULL);
       }
#endif

out:
       spin_unlock(&files->file_lock);
       return error;
}
rcu_assign_pointer()は、fdt->fd[fd]=fileとなります。fdt->fd[]はRCUでの実装となっています。fdt->fd[fd]=NULLで、掛かるstruct fileを他のプロセスがread中である考慮の必要ありません。
void fd_install(unsigned int fd, struct file *file)
{
       struct files_struct *files = current->files;
       struct fdtable *fdt;
       spin_lock(&files->file_lock);
       fdt = files_fdtable(files);
       BUG_ON(fdt->fd[fd] != NULL);
       rcu_assign_pointer(fdt->fd[fd], file);
       spin_unlock(&files->file_lock);
}
rlimit(RLIMIT_NOFILE)はカレントプロセスの最大オープンファイル数です。sysctl_nr_openはカーネル全プロセスの最大オープンファイル数です。デフォルトは1024*1024の1Mです。この最大値より取得開始位置が大きければ、これ以上取得できません。

struct fdtableの最大数より小さければ取得済みということで、それ以外expand_fdtable()でfdtableを拡張します。
int sysctl_nr_open __read_mostly = 1024*1024;

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);
}
alloc_fdtable()はnrの応じたstruct fdtableを取得します。そのfdtableにカレントのfdtableを上書きした後、files->fdt=new_fdtとし、カレントだったfdtableを解放します。推測ですが、以前はfdtable->nextに新規取得したfdtableをリストすることで管理していたのでないかと。現状はfdtable->nextは未使用です。実装的には毎回、新規fdtableに旧fdtableを複写し、旧fdtableを解放しますが、ファイルIDの検索/取得と言う点では、1つのfdtableで管理した方が効率的です。
static int expand_fdtable(struct files_struct *files, int nr)
       __releases(files->file_lock)
       __acquires(files->file_lock)
{
       struct fdtable *new_fdt, *cur_fdt;

       spin_unlock(&files->file_lock);
       new_fdt = alloc_fdtable(nr);
       spin_lock(&files->file_lock);
       if (!new_fdt)
               return -ENOMEM;

       if (unlikely(new_fdt->max_fds <= nr)) {
               __free_fdtable(new_fdt);
               return -EMFILE;
       }

       cur_fdt = files_fdtable(files);
       if (nr >= cur_fdt->max_fds) {
               /* Continue as planned */
               copy_fdtable(new_fdt, cur_fdt);
               rcu_assign_pointer(files->fdt, new_fdt);
               if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
                       free_fdtable(cur_fdt);
       } else {
               /* Somebody else expanded, so undo our attempt */
               __free_fdtable(new_fdt);
       }
       return 1;
}
引数のnrの拡張サイズを補正します。これはfdt->fdはpageサイズ(4k)およびfdt->open_fds/fdt->close_on_execのビット管理のため。と思います。nrを1K単位のファイル数 nr /= (1024 / sizeof(struct file *))として拡張し、2のべき乗となるファイル数 nr /= roundup_pow_of_two(nr + 1)へと補正し、その補正ファイル数でもって改めて拡張サイズ nr *= (1024 / sizeof(struct file *))といたします。

なお、ビット管理のfdt->open_fds/fdt->close_on_execは、拡張サイズの2倍とし、先頭をfdt->open_fdsへ、中間以降をfdt->close_on_execとします。
static struct fdtable * alloc_fdtable(unsigned int nr)
{
       struct fdtable *fdt;
       char *data;

       nr /= (1024 / sizeof(struct file *));
       nr = roundup_pow_of_two(nr + 1);
       nr *= (1024 / sizeof(struct file *));

       if (unlikely(nr > sysctl_nr_open))
               nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;

       fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
       if (!fdt)
               goto out;
       fdt->max_fds = nr;
       data = alloc_fdmem(nr * sizeof(struct file *));
       if (!data)
               goto out_fdt;
       fdt->fd = (struct file **)data;
       data = alloc_fdmem(max_t(unsigned int,
                                2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
       if (!data)
               goto out_arr;
       fdt->open_fds = (fd_set *)data;
       data += nr / BITS_PER_BYTE;
       fdt->close_on_exec = (fd_set *)data;
       fdt->next = NULL;

       return fdt;

out_arr:
       free_fdmem(fdt->fd);
out_fdt:
       kfree(fdt);
out:
       return NULL;
}

補足

システムとしての全ファイル数のsysctl_nr_open
[root@localhost kitamura]# cat /proc/sys/fs/nr_open
1048576

プロセス毎の全ファイル数は、setrlimit/getrlimitシステムコールで行えます。
#define INR_OPEN_CUR 1024       /* Initial setting for nfile rlimits */
#define INR_OPEN_MAX 4096       /* Hard limit for nfile rlimits */

struct rlimit {
       unsigned long   rlim_cur;
       unsigned long   rlim_max;
};

#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 },   \
}

static inline unsigned long task_rlimit_max(const struct task_struct *tsk,
               unsigned int limit)
{
       return ACCESS_ONCE(tsk->signal->rlim[limit].rlim_max);
}

最終更新 2015/01/06 20:49:22 - north
(2010/01/15 17:50:21 作成)


検索

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