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で更新します。
O_NONBLOCKでは、シグナルが無いとエラーになりますが、複数シグナル受信の場合、最初のシグナルが受信できれば、以降nonblock = 1とし、残りのシグナルはウエイトする事で受信します。
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マクロを介しての以下の実装となっています。
シグナル数は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は定数で、リンク時にエラーとなります。
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(¤t->sighand->siglock);
ctx->sigmask = sigmask;
spin_unlock_irq(¤t->sighand->siglock);
wake_up(¤t->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(¤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;
}
追記
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(); \
} \
}





