signalfd


Rev.2を表示中。最新版はこちら

flagsはSFD_CLOEXECとSFD_NONBLOCKのみ設定可能で、SIGKILLとSIGSTOPは取得できないようになっています。そうでないとkillされないプロセスを無限に作成されることになり、セキュリティ的によろしくありません。

ufd==-1の時、anon_inode_getfd()で[signalfd]をdentry名、signalfd_fopsをfileオペレーションコールバックとし,file->private_dataにctxが設定とする新規fileIDおよびファイルディスクリプタを取得します。ufd!=-1の時、ctx->sigmask = sigmasとし、作成済みsignalfdのシグナルマスクを設定することになります。
#define _sig_not(x)     (~(x))
#define SFD_CLOEXEC O_CLOEXEC
#define SFD_NONBLOCK O_NONBLOCK

static const struct file_operations signalfd_fops = {
       .release        = signalfd_release,
       .poll           = signalfd_poll,
       .read           = signalfd_read,
       .llseek         = noop_llseek,
};

SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
               size_t, sizemask, int, flags)
{
       sigset_t sigmask;
       struct signalfd_ctx *ctx;

       /* Check the SFD_* constants for consistency.  */
       BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC);
       BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK);

       if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK))
               return -EINVAL;

       if (sizemask != sizeof(sigset_t) ||
           copy_from_user(&sigmask, user_mask, sizeof(sigmask)))
               return -EINVAL;
       sigdelsetmask(&sigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
       signotset(&sigmask);

       if (ufd == -1) {
               ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
               if (!ctx)
                       return -ENOMEM;

               ctx->sigmask = sigmask;

               ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx,
                                      O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK)));
               if (ufd < 0)
                       kfree(ctx);
       } else {
               struct file *file = fget(ufd);
               if (!file)
                       return -EBADF;
               ctx = file->private_data;
               if (file->f_op != &signalfd_fops) {
                       fput(file);
                       return -EINVAL;
               }
               spin_lock_irq(&current->sighand->siglock);
               ctx->sigmask = sigmask;
               spin_unlock_irq(&current->sighand->siglock);

               wake_up(&current->sighand->signalfd_wqh);
               fput(file);
       }

       return ufd;
}
引数の読み込みサイズcount /= sizeof(struct signalfd_siginfo)から読み込むシグナル数、signalfd_dequeue()でプロセスがペンディングしているシグナルを、siginfo = (struct signalfd_siginfo __user *) bufに設定します。
static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
                            loff_t *ppos)
{
       struct signalfd_ctx *ctx = file->private_data;
       struct signalfd_siginfo __user *siginfo;
       int nonblock = file->f_flags & O_NONBLOCK;
       ssize_t ret, total = 0;
       siginfo_t info;

       count /= sizeof(struct signalfd_siginfo);
       if (!count)
               return -EINVAL;

       siginfo = (struct signalfd_siginfo __user *) buf;
       do {
               ret = signalfd_dequeue(ctx, &info, nonblock);
               if (unlikely(ret <= 0))
                       break;
               ret = signalfd_copyinfo(siginfo, &info);
               if (ret < 0)
                       break;
               siginfo++;
               total += ret;
               nonblock = 1;
       } while (--count);

       return total ? total: ret;
}

dequeue_signal()でcurrent->pendingのペンディングのシグナルをinfoに設定し、取得したシグナル番号を返します。この時current->pendingのシグナルは取り除かれます。返り値が0なら取得するシグナルがありません。nonblockが0ならエラーとなるのですが、signalfdはSFD_NONBLOCKだとエラー故、nonblock==0はありえないのですが・・・

nonblock=1なら、TASK_INTERRUPTIBLEとし、シグナルをウエイトします。シグナルがプロセスに送られました。dequeue_signal()でそのシグナルを取得します。もしctx->sigmaskでないシグナルなら取得できません。signal_pending()でプロセスがシグナルを取得することになり、システムコール中にシグナルで処理が中止された旨のエラーとしてERESTARTSYSを返します。
static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info,
                               int nonblock)
{
       ssize_t ret;
       DECLARE_WAITQUEUE(wait, current);

       spin_lock_irq(&current->sighand->siglock);
       ret = dequeue_signal(current, &ctx->sigmask, info);
       switch (ret) {
       case 0:
               if (!nonblock)
                       break;
               ret = -EAGAIN;
       default:
               spin_unlock_irq(&current->sighand->siglock);
               return ret;
       }

       add_wait_queue(&current->sighand->signalfd_wqh, &wait);
       for (;;) {
               set_current_state(TASK_INTERRUPTIBLE);
               ret = dequeue_signal(current, &ctx->sigmask, info);
               if (ret != 0)
                       break;
               if (signal_pending(current)) {
                       ret = -ERESTARTSYS;
                       break;
               }
               spin_unlock_irq(&current->sighand->siglock);
               schedule();
               spin_lock_irq(&current->sighand->siglock);
       }
       spin_unlock_irq(&current->sighand->siglock);

       remove_wait_queue(&current->sighand->signalfd_wqh, &wait);
       __set_current_state(TASK_RUNNING);

       return ret;
}

補足

読み込むシグナル情報は、シグナルコードによって以下のようになっています。ユーザプロセスから送られるシグナルは、killシステムコールを介してで、__SI_KILLとなります。
static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
                            siginfo_t const *kinfo)
{
       long err;

       BUILD_BUG_ON(sizeof(struct signalfd_siginfo) != 128);


       err = __clear_user(uinfo, sizeof(*uinfo));


       err |= __put_user(kinfo->si_signo, &uinfo->ssi_signo);
       err |= __put_user(kinfo->si_errno, &uinfo->ssi_errno);
       err |= __put_user((short) kinfo->si_code, &uinfo->ssi_code);
       switch (kinfo->si_code & __SI_MASK) {
       case __SI_KILL:
               err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
               err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
               break;
       case __SI_TIMER:
                err |= __put_user(kinfo->si_tid, &uinfo->ssi_tid);
                err |= __put_user(kinfo->si_overrun, &uinfo->ssi_overrun);
                err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
                err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
               break;
       case __SI_POLL:
               err |= __put_user(kinfo->si_band, &uinfo->ssi_band);
               err |= __put_user(kinfo->si_fd, &uinfo->ssi_fd);
               break;
       case __SI_FAULT:
               err |= __put_user((long) kinfo->si_addr, &uinfo->ssi_addr);
#ifdef __ARCH_SI_TRAPNO
               err |= __put_user(kinfo->si_trapno, &uinfo->ssi_trapno);
#endif
#ifdef BUS_MCEERR_AO
               if (kinfo->si_code == BUS_MCEERR_AR ||
                   kinfo->si_code == BUS_MCEERR_AO)
                       err |= __put_user((short) kinfo->si_addr_lsb,
                                         &uinfo->ssi_addr_lsb);
#endif
               break;
       case __SI_CHLD:
               err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
               err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
               err |= __put_user(kinfo->si_status, &uinfo->ssi_status);
               err |= __put_user(kinfo->si_utime, &uinfo->ssi_utime);
               err |= __put_user(kinfo->si_stime, &uinfo->ssi_stime);
               break;
       case __SI_RT: /* This is not generated by the kernel as of now. */
       case __SI_MESGQ: /* But this is */
               err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
               err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
               err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
               err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
               break;
       default:
               err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
               err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
               err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
               err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
               break;
       }

       return err ? -EFAULT: sizeof(*uinfo);

備考

#define SFD_CLOEXEC O_CLOEXEC/#define SFD_NONBLOCK O_NONBLOCKとなっています。if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK))でreturn -EINVALとなり、anon_inode_getfd()でflags & (O_CLOEXEC | O_NONBLOCK))としているのは冗長的ではと・・・? また、signotset()でsigmaskのflagビットを反転させていますが、特に意味はないようです。


最終更新 2014/04/27 19:24:44 - north
(2014/04/27 18:36:35 作成)


検索

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