select


selectはpollと同じ機能です。pollと異なりselectするファイルIDをビットで管理します。セットする領域をlong配列とし、ファイルID番号が配列0を起点とするlongビットサイズに応じた配列のビット位置になります。従って配列0の0ビットから、selectするファイル数まで順に走査することになります。

selectできるFILE ID数は、デフォルトで1024で、これはコンパイル時__FD_SETSIZEを定義することで、任意に拡張できます。デフォルトでlongが64ビットなら、fds_bits[1024/8*8=16]です。__FD_SETSIZEはカーネルマターで、ユーザサイドのFD_SET()関数はシステムコールでなく、gccマターでFD_SET()の引数の上限のチェックは行なわれません。
#undef __NFDBITS
#define __NFDBITS       (8 * sizeof(unsigned long))

#undef __FD_SETSIZE
#define __FD_SETSIZE    1024

#undef __FDSET_LONGS
#define __FDSET_LONGS   (__FD_SETSIZE/__NFDBITS)

typedef struct {
       unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;


typedef __kernel_fd_set         fd_set;
#define FD_SET(fd,fdsetp)       __FD_SET(fd,fdsetp)

static inline void __FD_SET(unsigned long __fd, __kernel_fd_set *__fdsetp)
{
       unsigned long __tmp = __fd / __NFDBITS;
       unsigned long __rem = __fd % __NFDBITS;
       __fdsetp->fds_bits[__tmp] |= (1UL<<__rem);
}
fd_setのinp/outp/expにファイル状態の入力/出力/例外のselectするファイルIDを、nはこの3つの中でファイル数の一番大きいファイル数が設定されます。
SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
               fd_set __user *, exp, struct timeval __user *, tvp)
{
       struct timespec end_time, *to = NULL;
       struct timeval tv;
       int ret;

       if (tvp) {
               if (copy_from_user(&tv, tvp, sizeof(tv)))
                       return -EFAULT;

               to = &end_time;
               if (poll_select_set_timeout(to,
                               tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
                               (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
                       return -EINVAL;
       }

       ret = core_sys_select(n, inp, outp, exp, to);
       ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);

       return ret;
}
ユーザ空間引数のfd_set inp/outp/expをカーネル空間のlong stack_fds[SELECT_STACK_ALLOC/sizeof(long)]に設定します。SELECT_STACK_ALLOC=256バイトの最大ファイル数256×8=2048とするstack_fds[]の配列となります。

なお、走査するファイル数が、2048を超えるカーネル下では、バイト単位のファイル数の6倍(in/out/ex/res_in/res_out/res_exの各領域分)したサイズでkmalloc()で動的に取得することになります。
#define MAX_STACK_ALLOC 832
#define FRONTEND_STACK_ALLOC    256
#define SELECT_STACK_ALLOC      FRONTEND_STACK_ALLOC

typedef struct {
       unsigned long *in, *out, *ex;
       unsigned long *res_in, *res_out, *res_ex;
} fd_set_bits;

#define FDS_BITPERLONG  (8*sizeof(long))
#define FDS_LONGS(nr)   (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
#define FDS_BYTES(nr)   (FDS_LONGS(nr)*sizeof(long))

int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
                          fd_set __user *exp, struct timespec *end_time)
{
       fd_set_bits fds;
       void *bits;
       int ret, max_fds;
       unsigned int size;
       struct fdtable *fdt;
       long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];

       ret = -EINVAL;
       if (n < 0)
               goto out_nofds;

       rcu_read_lock();
       fdt = files_fdtable(current->files);
       max_fds = fdt->max_fds;
       rcu_read_unlock();
       if (n > max_fds)
               n = max_fds;

       size = FDS_BYTES(n);
       bits = stack_fds;
       if (size > sizeof(stack_fds) / 6) {
               ret = -ENOMEM;
               bits = kmalloc(6 * size, GFP_KERNEL);
               if (!bits)
                       goto out_nofds;
       }
       fds.in      = bits;
       fds.out     = bits +   size;
       fds.ex      = bits + 2*size;
       fds.res_in  = bits + 3*size;
       fds.res_out = bits + 4*size;
       fds.res_ex  = bits + 5*size;

       if ((ret = get_fd_set(n, inp, fds.in)) ||
           (ret = get_fd_set(n, outp, fds.out)) ||
           (ret = get_fd_set(n, exp, fds.ex)))
               goto out;
       zero_fd_set(n, fds.res_in);
       zero_fd_set(n, fds.res_out);
       zero_fd_set(n, fds.res_ex);

       ret = do_select(n, &fds, end_time);

       if (ret < 0)
               goto out;
       if (!ret) {
               ret = -ERESTARTNOHAND;
               if (signal_pending(current))
                       goto out;
               ret = 0;
       }

       if (set_fd_set(n, inp, fds.res_in) ||
           set_fd_set(n, outp, fds.res_out) ||
           set_fd_set(n, exp, fds.res_ex))
               ret = -EFAULT;

out:
       if (bits != stack_fds)
               kfree(bits);
out_nofds:
       return ret;
}
for (i = 0; i < n; ++rinp, ++routp, ++rexp)で、ファイルIDの最大値に対応するfds_bits[]インデックスのnまで、long単位のfor (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1)でビット単位で走査するファイルIDを取得します。

ファイルIDのpollオペレーションコールバックでファイル状態変化を取得し、それがPOLLIN_SET/POLLOUT_SET/POLLEX_SETに応じて、結果としてそのファイルIDをfds->res_in/fds->res_out/fds->res_exにセットし、retval++を状態変化したファイル数でシステムコールの返り値とします。なお、状態変化取得時時、wait = NULLとすることで、pollオペレーションコールバックではwaitキューにリストされません。

一連のチェックを終えると、if (retval || timed_out || signal_pending(current))で、状態変化/タイムアウト/シグナル受信なら、復帰しそうでないなら、poll_schedule_timeout()を介してループし続けます。
int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
       ktime_t expire, *to = NULL;
       struct poll_wqueues table;
       poll_table *wait;
       int retval, i, timed_out = 0;
       unsigned long slack = 0;

       rcu_read_lock();
       retval = max_select_fd(n, fds);
       rcu_read_unlock();

       if (retval < 0)
               return retval;
       n = retval;

       poll_initwait(&table);
       wait = &table.pt;
       if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
               wait = NULL;
               timed_out = 1;
       }

       if (end_time && !timed_out)
               slack = select_estimate_accuracy(end_time);

       retval = 0;
       for (;;) {
               unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;

               inp = fds->in; outp = fds->out; exp = fds->ex;
               rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;

               for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
                       unsigned long in, out, ex, all_bits, bit = 1, mask, j;
                       unsigned long res_in = 0, res_out = 0, res_ex = 0;
                       const struct file_operations *f_op = NULL;
                       struct file *file = NULL;

                       in = *inp++; out = *outp++; ex = *exp++;
                       all_bits = in | out | ex;
                       if (all_bits == 0) {
                               i += __NFDBITS;
                               continue;
                       }

                       for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
                               int fput_needed;
                               if (i >= n)
                                       break;
                               if (!(bit & all_bits))
                                       continue;
                               file = fget_light(i, &fput_needed);
                               if (file) {
                                       f_op = file->f_op;
                                       mask = DEFAULT_POLLMASK;
                                       if (f_op && f_op->poll) {
                                               wait_key_set(wait, in, out, bit);
                                               mask = (*f_op->poll)(file, wait);
                                       }
                                       fput_light(file, fput_needed);
                                       if ((mask & POLLIN_SET) && (in & bit)) {
                                               res_in |= bit;
                                               retval++;
                                               wait = NULL;
                                       }
                                       if ((mask & POLLOUT_SET) && (out & bit)) {
                                               res_out |= bit;
                                               retval++;
                                               wait = NULL;
                                       }
                                       if ((mask & POLLEX_SET) && (ex & bit)) {
                                               res_ex |= bit;
                                               retval++;
                                               wait = NULL;
                                       }
                               }
                       }
                       if (res_in)
                               *rinp = res_in;
                       if (res_out)
                               *routp = res_out;
                       if (res_ex)
                               *rexp = res_ex;
                       cond_resched();
               }
               wait = NULL;
               if (retval || timed_out || signal_pending(current))
                       break;
               if (table.error) {
                       retval = table.error;
                       break;
               }

               if (end_time && !to) {
                       expire = timespec_to_ktime(*end_time);
                       to = &expire;
               }

               if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
                                          to, slack))
                       timed_out = 1;
       }

       poll_freewait(&table);

       return retval;
}

備考

シグナルでselectを脱する時、返値は状態変化したファイルID数で、pollと異なりEINTとなりません。呼び出し元のcore_sys_selec()では、do_select()でファイル状態変化なしでシグナルにより復帰した時(ret=0)は、ret = -ERESTARTNOHANDとしていますが、一つでもファイル状態変化があってシグナルで復帰すれば、その数となります。

ファイルIDがfd_set_bitsのビット位置となるため、selectするファイルとしないファイルの混在する実装では、selectするファイルは最初にオープンすべきのようです。


最終更新 2015/05/30 17:51:27 - north
(2015/05/30 17:48:52 作成)


検索

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