poll


timeout_msecsが設定されていれば、poll_select_set_timeout()でカレント時間に終了時間timeout_msecsを加算した終了時刻をstruct timespecに設定し、do_sys_poll()でfileIDの状態変化およびシグナルが発生するまでウエイトします。timeout_msecsがマイナスならto=NULLで、タイムアウトしません。

シグナルが発生したら-EINTR、タイムアウトなら0、fileID状態変化なら状態変化したfileID数がretになります。

ret == -EINTRなら、current_thread_info()->restart_blockにpoll引数を、そしてrestart_block->fn = do_restart_pollとし、ret = -ERESTART_RESTARTBLOCKとし、条件がととのへば、ユーザプロセスへの復帰時、restart_syscallシステムコールをコールし、do_restart_poll()がコールされ残った時間分pollします。
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
               int, timeout_msecs)
{
       struct timespec end_time, *to = NULL;
       int ret;

       if (timeout_msecs >= 0) {
               to = &end_time;
               poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,
                       NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
       }

       ret = do_sys_poll(ufds, nfds, to);

       if (ret == -EINTR) {
               struct restart_block *restart_block;

               restart_block = &current_thread_info()->restart_block;
               restart_block->fn = do_restart_poll;
               restart_block->poll.ufds = ufds;
               restart_block->poll.nfds = nfds;

               if (timeout_msecs >= 0) {
                       restart_block->poll.tv_sec = end_time.tv_sec;
                       restart_block->poll.tv_nsec = end_time.tv_nsec;
                       restart_block->poll.has_timeout = 1;
               } else
                       restart_block->poll.has_timeout = 0;

               ret = -ERESTART_RESTARTBLOCK;
       }
       return ret;
}
long stack_pps[POLL_STACK_ALLOC/sizeof(long)]をメモリとするstruct poll_list *const headのentries[]にユーザパラメータのstruct pollfd __user *ufdsを複写します。entries[]の配列はN_STACK_PPSとなります。もしnfdsがN_STACK_PPSより多ければ、kmalloc()で動的にstruct poll_listのメモリを確保し、先のpoll_list->nextにリストし、そのentries[]にstruct pollfd __user *ufdsをセットします。kmalloc()での実装の負荷を軽減するべく、しかも余分な静的メモリ消費を防ぐための実装で、pollの本質と関係ありません。

poll_initwait()でpoll_wqueues tableを初期化します。poll_wqueues tableは、do_poll()で各FILEオペレーションコールバックpollの引数となり、ウエイトするFILE IDがリストされます。
#define FRONTEND_STACK_ALLOC    256
#define SELECT_STACK_ALLOC      FRONTEND_STACK_ALLOC
#define POLL_STACK_ALLOC        FRONTEND_STACK_ALLOC

#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list))  / \
                       sizeof(struct pollfd))

struct poll_list {
       struct poll_list *next;
       int len;
       struct pollfd entries[0];
};

int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
               struct timespec *end_time)
{
       struct poll_wqueues table;
       int err = -EFAULT, fdcount, len, size;

       long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
       struct poll_list *const head = (struct poll_list *)stack_pps;
       struct poll_list *walk = head;
       unsigned long todo = nfds;

       if (nfds > rlimit(RLIMIT_NOFILE))
               return -EINVAL;

       len = min_t(unsigned int, nfds, N_STACK_PPS);
       for (;;) {
               walk->next = NULL;
               walk->len = len;
               if (!len)
                       break;

               if (copy_from_user(walk->entries, ufds + nfds-todo,
                                       sizeof(struct pollfd) * walk->len))
                       goto out_fds;

               todo -= walk->len;
               if (!todo)
                       break;

               len = min(todo, POLLFD_PER_PAGE);
               size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;
               walk = walk->next = kmalloc(size, GFP_KERNEL);
               if (!walk) {
                       err = -ENOMEM;
                       goto out_fds;
               }
       }

       poll_initwait(&table);
       fdcount = do_poll(nfds, head, &table, end_time);
       poll_freewait(&table);

       for (walk = head; walk; walk = walk->next) {
               struct pollfd *fds = walk->entries;
               int j;

               for (j = 0; j < walk->len; j++, ufds++)
                       if (__put_user(fds[j].revents, &ufds->revents))
                               goto out_fds;
       }

       err = fdcount;
out_fds:
       walk = head->next;
       while (walk) {
               struct poll_list *pos = walk;
               walk = walk->next;
               kfree(pos);
       }

       return err;
}
最初のifはウエイトしないケースでpt = NULLは、ファイルオペレーションpollコールバックで、ウエイトキューにリストされないという事です。次のifはウエイトするケースでstackがウエイトタイムです。

poll_list->nextの順に、そのentries[]のFILE IDをpfdに設定し、do_pollfd()でpollします。状態変化があればそのevent IDが返ってきて、ブレイクせず残りのFILE IDをチェックし続けます。pt = NULLとする事で、以降のFILE IDは状態変化がなくてもウエイトリストされず、任意1つ状態変化があれば必ず復帰する事になります。countは返り値で、プラスの場合、pollシステムコールの返値は状態変化のFILEの数ということです。(通常は1ですが。沢山ソケットのようなpollでは、1以上の結果となる事が想定されます。)

全FILE IDに状態変化がなければ、signal_pending()でシグナルをチェックし、受信していれば返り値が-EINTRとなります。以降poll_schedule_timeout()を介してループし、返り値が0ならslackと時間となり、timed_out = 1でcount = 0でループをブレイクします。
static int do_poll(unsigned int nfds,  struct poll_list *list,
                  struct poll_wqueues *wait, struct timespec *end_time)
{
       poll_table* pt = &wait->pt;
       ktime_t expire, *to = NULL;
       int timed_out = 0, count = 0;
       unsigned long slack = 0;

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

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

       for (;;) {
               struct poll_list *walk;

               for (walk = list; walk != NULL; walk = walk->next) {
                       struct pollfd * pfd, * pfd_end;

                       pfd = walk->entries;
                       pfd_end = pfd + walk->len;
                       for (; pfd != pfd_end; pfd++) {
                               if (do_pollfd(pfd, pt)) {
                                       count++;
                                       pt = NULL;
                               }
                       }
               }

               pt = NULL;
               if (!count) {
                       count = wait->error;
                       if (signal_pending(current))
                               count = -EINTR;
               }
               if (count || timed_out)
                       break;

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

               if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))
                       timed_out = 1;
       }
       return count;
}

追記

シグナルでpollを脱しても、システムコール再実行で、タイムアウトまでpollし続けるとの理解ですが、以下の検証では、そのような結果となりません。シグナルマターかと思いますが、頃合いを見計らって検証したいと思っていますが・・・。ご指摘あれば幸いです。
#include <sys/syscall.h>
#include <stdio.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>

void    do_poll();
void    poll_stdin(void);
static int select_stdin(void);

int main()
{
       int     p_id;

       if ((p_id = fork()) == 0) {
               do_poll();
       }
       else {
               sleep(1);
               kill(p_id, SIGUSR1);
               wait(NULL);
       }
       return 0;
}

void sig_hand(int sig)
{
       char    *msg = "signal\n";
       write(1, msg, 8);
}

void    do_poll()
{
       struct sigaction act;

       sigemptyset(&act.sa_mask);
       act.sa_handler = sig_hand;
       act.sa_flags = SA_RESTART;

       sigaction(SIGUSR1, &act, NULL);
       poll_stdin();
}

void    poll_stdin(void)
{
       int rc = 0;
       struct pollfd fds[1];

       fds[0].fd      = 0;
       fds[0].events  = POLLIN;
       fds[0].revents = 0;

       rc = syscall(SYS_poll, fds, 1, 10000);
       printf("after poll:%d ", rc);
       if(rc < 0){
               printf("%s\n", strerror(errno));
       }
       if(rc == 0){
               printf("timeout\n");
       }
}
結果
[root@localhost kitamura]# ./a.out
signal
after poll:-1 Interrupted system call
下記の結果を想定していたのですが・・・
signal
after poll:0 timeout

備考

ppollはpollウエイト中の、シグナルをマスクコントロール(ファイル状態変化後に、シグナル受信の保障)する機能を付加した物で、本質的な機能はpollと同じです。


最終更新 2015/05/25 01:27:15 - north
(2015/05/25 01:27:15 作成)


検索

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