signalfd
Rev.1を表示中。最新版はこちら。
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(¤t->sighand->siglock); ctx->sigmask = sigmask; spin_unlock_irq(¤t->sighand->siglock); wake_up(¤t->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(¤t->sighand->siglock); ret = dequeue_signal(current, &ctx->sigmask, info); switch (ret) { case 0: if (!nonblock) break; ret = -EAGAIN; default: spin_unlock_irq(¤t->sighand->siglock); return ret; } add_wait_queue(¤t->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(¤t->sighand->siglock); schedule(); spin_lock_irq(¤t->sighand->siglock); } spin_unlock_irq(¤t->sighand->siglock); remove_wait_queue(¤t->sighand->signalfd_wqh, &wait); __set_current_state(TASK_RUNNING); return ret; }