signalfd


flagはSFD_CLOEXEC | SFD_NONBLOCK以外は設定できません。anon_inode_getfd()でanon_inodefsからinodeそしてFILE取得し、file->f_flags=flagとします。

sigmaskにSIGKILL/SIGSTOPを追加し、コーディング上の実装故で、受信シグナル属性はビット単位で設定されており、signotset()で受信するシグナル属性を反転します。

引数ufd==-1なら、signalfd_fopsをコールバックとする新規FILEを取得し、そうでないならsignalfdからFILEを取得し、file->private_data=ctx、あるいはctx->sigmask = sigmaskで更新します。
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;

       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はbuffサイズで、count /= sizeof(struct signalfd_siginfo)で所得するシグナル数とし、signalfd_dequeue()でcount個となるまでシグナルを取得すべくループします。

O_NONBLOCKでは、シグナルが無いとエラーになりますが、複数シグナル受信の場合、最初のシグナルが受信できれば、以降nonblock = 1とし、残りのシグナルはウエイトする事で受信します。
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;
}
currentのstruct sigpending pendingから、ctx->sigmaskのシグナルを取得します。返値は取得したシグナルNOです。0なら取得しておらず、ブロックしないならエラーですが、ブロックするならカレントプロセスをwaiteキューにリストし、シグナルが取得できるまでschedule()を介してループし続けます。
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;
}

追記

BUILD_BUG_ONマクロはconditionが真(1)なら、!!(condition)=1となり、配列char[-1]でコンパイルエラーです。!!(condition)でなく(condition)でいいのでは?と思いますが、BUILD_BUG_ON(a=4-3-1)と言った定数としてconditionを取り扱うこともできるようにするためです。
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

SFD_CLOEXEC != O_CLOEXEC及びSFD_NONBLOCK != O_NONBLOCKなら、コンパイル時にエラーとなります。カーネルコンパイル時、_LINUX_SIGNALFD_Hを定義すれば、#define SFD_CLOEXEC O_CLOEXEC/#define SFD_NONBLOCK O_NONBLOCKとされ、_LINUX_SIGNALFD_Hをundefineとすることで、signalfdを独自に実装するとか、ないし実装しない実装を考慮してかと思いますが、この必要性の実効性の有無は理解不能です。

signotset()は、ビット単位で設定してあるシグナル情報を反転するだけですが、_SIG_SET_OPマクロを介しての以下の実装となっています。
longsigset_t sigmask

typedef struct {
       unsigned long sig[_NSIG_WORDS];
} sigset_t;

#define _NSIG           64
#define _NSIG_BPW       __BITS_PER_LONG
#define _NSIG_WORDS     (_NSIG / _NSIG_BPW)

#define _sig_not(x)     (~(x))
_SIG_SET_OP(signotset, _sig_not)

#define _SIG_SET_OP(name, op)                                           \
static inline void name(sigset_t *set)                                  \
{                                                                       \
       extern void _NSIG_WORDS_is_unsupported_size(void);              \
                                                                       \
       switch (_NSIG_WORDS) {                                          \
           case 4: set->sig[3] = op(set->sig[3]);                      \
                   set->sig[2] = op(set->sig[2]);                      \
           case 2: set->sig[1] = op(set->sig[1]);                      \
           case 1: set->sig[0] = op(set->sig[0]);                      \
                   break;                                              \
           default:                                                    \
               _NSIG_WORDS_is_unsupported_size();                      \
       }                                                               \
}
結果的に以下の実装になります。目的は、ビット情報としてのシグナル属性を、long型の配列とする事にあり、配列数がシグナル数およびlongのビットサイズに依存するからです。

シグナル数は64で、longが64ビットなら配列は1、32ビットなら配列は2と、_NSIG_WORDSがシステムに応じた配列数となります。で配列数に応じて各要素をただ単に反転するだけです。

配列数が4/2/1(long型が16/32/64ビット)でない場合、_NSIG_WORDS_is_unsupported_size()がコールされる事になりますが、_NSIG_WORDS_is_unsupported_size()は定義されておらず、inlineしかもswitchは定数で、リンク時にエラーとなります。
static inline void signotset(sigset_t *set)                                  \
{                                                                       \                                                                        \
       switch (_NSIG_WORDS) {                                          \
           case 4: set->sig[3] = ~set->sig[3];                      \
                   set->sig[2] = ~set->sig[2];                      \
           case 2: set->sig[1] = ~set->sig[1];                      \
           case 1: set->sig[0] = ~set->sig[0];                      \
                   break;                                              \
           default:                                                    \
               _NSIG_WORDS_is_unsupported_size();                      \
       }                                                               \
}


最終更新 2015/05/16 22:21:44 - north
(2014/04/27 18:36:35 作成)


検索

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