waitfd
RFC PATCH v2 1/1 waitfd
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of siginfo_t objects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.
Patch is against v2.6.28
--CJD
arch/x86/include/asm/unistd_32.h +0 -0
@@ -338,6 +338,7 @@
#define __NR_dup3 330
#define __NR_pipe2 331
#define __NR_inotify_init1 332
+#define __NR_waitfd 333
#ifdef __KERNEL__
arch/x86/include/asm/unistd_64.h +0 -0
@@ -653,6 +653,8 @@ __SYSCALL(__NR_dup3, sys_dup3)
__SYSCALL(__NR_pipe2, sys_pipe2)
#define __NR_inotify_init1 294__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+#define __NR_waitfd 295
+__SYSCALL(__NR_waitfd, sys_waitfd)
#ifndef __NO_STUBS
arch/x86/kernel/syscall_table_32.S +1 -0
@@ -332,3 +332,4 @@ ENTRY(sys_call_table) .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 + .long sys_waitfd
fs/Makefile +0 -0
@@ -25,6 +25,7 @@ obj-$(CONFIG_INOTIFY_USER) += inotify_user.o
obj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_ANON_INODES) += anon_inodes.o
obj-$(CONFIG_SIGNALFD) += signalfd.o
+obj-$(CONFIG_WAITFD) += waitfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o
obj-$(CONFIG_AIO) += aio.o
fs/waitfd.c new file +117 -0
117 lines not on this page · raw message
init/Kconfig +10 -0
@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child state + changes from a file descriptor. + + If unsure, say Y. +
config SIGNALFD
bool "Enable signalfd() system call" if EMBEDDED select ANON_INODES
kernel/exit.c +14 -12
@@ -1233,18 +1233,20 @@ static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,int retval = rusagep ?getrusage (p,RUSAGE_BOTH , rusagep) :0 ;put_task_struct (p); -if (!retval) - retval =put_user (SIGCHLD , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user ((short )why, &infop->si_code); -if (!retval) - retval =put_user (pid, &infop->si_pid); -if (!retval) - retval =put_user (uid, &infop->si_uid); -if (!retval) - retval =put_user (status, &infop->si_status); +if (infop) { +if (!retval) + retval =put_user (SIGCHLD , &infop->si_signo); +if (!retval) + retval =put_user (0 , &infop->si_errno); +if (!retval) + retval =put_user ((short )why, &infop->si_code); +if (!retval) + retval =put_user (pid, &infop->si_pid); +if (!retval) + retval =put_user (uid, &infop->si_uid); +if (!retval) + retval =put_user (status, &infop->si_status); + }if (!retval) retval = pid;return retval;
@@ -1727,35 +1729,12 @@ repeat:
end:
current->state = TASK_RUNNING;
remove_wait_queue(¤t->signal->wait_chldexit,&wait);- if (infop) {
- if (retval > 0)
- retval = 0;
- else {
- /*
- * For a WNOHANG return, clear out all the fields - * we would set so the user can easily tell the - * difference. - */
- if (!retval)
- retval = put_user(0, &infop->si_signo);
- if (!retval)
- retval = put_user(0, &infop->si_errno);
- if (!retval)
- retval = put_user(0, &infop->si_code);
- if (!retval)
- retval = put_user(0, &infop->si_pid);
- if (!retval)
- retval = put_user(0, &infop->si_uid);
- if (!retval)
- retval = put_user(0, &infop->si_status);
- }
- }
return retval; }
-asmlinkage long sys_waitid(int which, pid_t upid,
- struct siginfo __user *infop, int options,
- struct rusage __user *ru)
+long do_waitid(int which, pid_t upid,
+ struct siginfo __user *infop, int options,
+ struct rusage __user *ru)
{
struct pid *pid = NULL;
enum pid_type type;
@@ -1789,6 +1768,39 @@ asmlinkage long sys_waitid(int which, pid_t upid,
ret = do_wait(type, pid, options, infop, NULL, ru);
put_pid(pid);+ return ret;
+}
+
+asmlinkage long sys_waitid(int which, pid_t upid,
+ struct siginfo __user *infop, int options,
+ struct rusage __user *ru)
+{
+ long ret;
+
+ ret = do_waitid(which, upid, infop, options, ru);
+
+ if (ret > 0)
+ ret = 0;
+ else {
+ /*
+ * For a WNOHANG return, clear out all the fields + * we would set so the user can easily tell the + * difference. + */
+ if (!ret)
+ ret = put_user(0, &infop->si_signo);
+ if (!ret)
+ ret = put_user(0, &infop->si_errno);
+ if (!ret)
+ ret = put_user(0, &infop->si_code);
+ if (!ret)
+ ret = put_user(0, &infop->si_pid);
+ if (!ret)
+ ret = put_user(0, &infop->si_uid);
+ if (!ret)
+ ret = put_user(0, &infop->si_status);
+ }
+
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(5, ret, which, upid, infop, options, ru);
return ret;kernel/sys_ni.c +0 -0
@@ -163,6 +163,7 @@ cond_syscall(sys_ioprio_set);
cond_syscall(sys_ioprio_get);
/* New file descriptors */
+cond_syscall(sys_waitfd);cond_syscall(sys_signalfd);cond_syscall(sys_signalfd4);cond_syscall(compat_sys_signalfd);
Quoted 7 lines
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
Same comment as last time - the right way to do this is to extend the existing notify APIs to the /proc file system
Quoted 33 lines · Casey Dahlin
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
--CJD
arch/x86/include/asm/unistd_32.h +1
-0@@ -338,6 +338,7 @@#define __NR_dup3 330#define __NR_pipe2 331#define __NR_inotify_init1 332 +#define __NR_waitfd 333#ifdef __KERNEL__arch/x86/include/asm/unistd_64.h +0
-0
arch/x86/include/asm/unistd_64.h __SYSCALL(__NR_dup3, sys_dup3) · line 658
Quoted 6 lines
arch/x86/include/asm/unistd_64.h +2
-0@@ -653,6 +653,8 @@ __SYSCALL(__NR_dup3, sys_dup3) __SYSCALL(__NR_pipe2, sys_pipe2)#define __NR_inotify_init1 294 __SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_waitfd 295 +__SYSCALL (__NR_waitfd, sys_waitfd)
Only for x86??
Quoted 49 lines
arch/x86/include/asm/unistd_64.h +0
-0#ifndef __NO_STUBSarch/x86/kernel/syscall_table_32.S +1
-0@@ -332,3 +332,4 @@ ENTRY(sys_call_table) .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 + .long sys_waitfdfs/Makefile +0
-0@@ -25,6 +25,7 @@ obj-$(CONFIG_INOTIFY_USER) += inotify_user.oobj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_ANON_INODES) += anon_inodes.o
obj-$(CONFIG_SIGNALFD) += signalfd.o+obj-$(CONFIG_WAITFD) += waitfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.oobj-$(CONFIG_AIO) += aio.o
fs/waitfd.c new file +19
-0@@ -0,0 +1,117 @@ +/* +* fs/waitfd.c +* +* Copyright (C) 2008 Red Hat, Casey Dahlin <[email protected]> +* +* Largely derived from fs/signalfd.c +*/ + +#include <linux/file.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/list.h> +#include <linux/anon_inodes.h> +#include <linux/syscalls.h> +
fs/waitfd.c line 29
Quoted 10 lines
fs/waitfd.c new file +10
-0+long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru); + +struct waitfd_ctx { +int ops; +int which; +pid_t upid; +}; +
Please use kernel coding style: use tabs to indent, not <lots-of-spaces>, and struct members, functions, etc., are indented by one tab stop minimum.
Quoted 44 lines
fs/waitfd.c new file +24
-0+static int waitfd_release (struct inode *inode,struct file *file) +{ +kfree (file->private_data); +return 0 ; +} + +static unsigned int waitfd_poll (struct file *file,poll_table *wait) +{ +struct waitfd_ctx *ctx = file->private_data; +long value; + +poll_wait (file, ¤t->signal->wait_chldexit, wait); + + value =do_waitid (ctx->which, ctx->upid,NULL , + ctx->ops |WNOHANG |WNOWAIT ,NULL ); +if (value >0 || value == -ECHILD ) +return POLLIN ; + +return 0 ; +} + +/* +* Returns a multiple of the size of a struct siginfo, or a negative +* error code. The "count" parameter must be at least sizeof(struct siginfo)
+ */
+staticssize_twaitfd_read(struct file *file, char__user*buf,size_tcount,fs/waitfd.c new file +16
-0+ loff_t *ppos) +{ +struct waitfd_ctx *ctx = file->private_data; +struct siginfo __user *info_addr = (struct siginfo *)buf; +int flags = ctx->ops; +ssize_t ret, total =0 ; + + count /=sizeof (struct siginfo ); +if (!count) +return -EINVAL ; + + do { + ret = do_waitid(ctx->which, ctx->upid, info_addr, flags, NULL); + if (ret == 0) + ret = -EAGAIN; + if (ret == -ECHILD)
fs/waitfd.c line 81
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ret =0 ; +if (ret <=0 ) +break ; + + info_addr++; + total +=sizeof (struct siginfo ); + flags |=WNOHANG ; + }while (--count); + +return total ? total: ret;
return total ? total : ret;
please.
Quoted 10 lines
fs/waitfd.c new file +10
-0+} + +static const struct file_operations waitfd_fops = { + .release = waitfd_release, + .poll = waitfd_poll, + .read = waitfd_read, +}; + +asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{
fs/waitfd.c line 101
Quoted 10 lines
fs/waitfd.c new file +10
-0+int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused; + +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ;
Use spaces around '|'.
Quoted 21 lines
fs/waitfd.c new file +16
-0+ + ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 ); +if (ufd <0 ) +kfree (ctx); + +return ufd; +}init/Kconfig +0
-0
init/Kconfig config EPOLL · line 691
Quoted 9 lines
init/Kconfig +6
-0@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child state
receiving
Kconfig help text should be indented by <tab><space><space>.
Quoted 136 lines
init/Kconfig +4
-0+ changes from a file descriptor. + + If unsure, say Y. +config SIGNALFD
bool "Enablesignalfd()system call" if EMBEDDED selectANON_INODESkernel/exit.c +14
-12@@ -1233,18 +1233,20 @@ static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid, int retval = rusagep ? getrusage(p, RUSAGE_BOTH, rusagep) : 0;put_task_struct (p); -if (!retval) - retval =put_user (SIGCHLD , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user ((short )why, &infop->si_code); -if (!retval) - retval =put_user (pid, &infop->si_pid); -if (!retval) - retval =put_user (uid, &infop->si_uid); -if (!retval) - retval =put_user (status, &infop->si_status); +if (infop) { +if (!retval) + retval =put_user (SIGCHLD , &infop->si_signo); +if (!retval) + retval =put_user (0 , &infop->si_errno); +if (!retval) + retval =put_user ((short )why, &infop->si_code); +if (!retval) + retval =put_user (pid, &infop->si_pid); +if (!retval) + retval =put_user (uid, &infop->si_uid); +if (!retval) + retval =put_user (status, &infop->si_status); + }if (!retval) retval = pid;return retval;kernel/exit.c +0
-0@@ -1727,35 +1729,12 @@ repeat:end:
current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit,&wait);kernel/exit.c +0
-23-if (infop) { -if (retval >0 ) - retval =0 ; -else { -/* -* For a WNOHANG return, clear out all the fields -* we would set so the user can easily tell the -* difference. -*/ -if (!retval) - retval =put_user (0 , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user (0 , &infop->si_code); -if (!retval) - retval =put_user (0 , &infop->si_pid); -if (!retval) - retval =put_user (0 , &infop->si_uid); -if (!retval) - retval =put_user (0 , &infop->si_status); - } - }return retval;}
kernel/exit.c +3
-3-asmlinkage long sys_waitid (int which,pid_t upid, -struct siginfo __user *infop,int options, -struct rusage __user *ru) +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru){ struct pid *pid = NULL; enum pid_type type;kernel/exit.c +33
-0@@ -1789,6 +1768,39 @@ asmlinkage long sys_waitid(int which, pid_t upid, ret =do_wait (type, pid, options, infop,NULL , ru);put_pid (pid); +return ret; +} + +asmlinkage long sys_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) +{ +long ret; + + ret =do_waitid (which, upid, infop, options, ru); + +if (ret >0 ) + ret =0 ; +else { +/* +* For a WNOHANG return, clear out all the fields +* we would set so the user can easily tell the +* difference. +*/ +if (!ret) + ret =put_user (0 , &infop->si_signo); +if (!ret) + ret =put_user (0 , &infop->si_errno); +if (!ret) + ret =put_user (0 , &infop->si_code); +if (!ret) + ret =put_user (0 , &infop->si_pid); +if (!ret) + ret =put_user (0 , &infop->si_uid); +if (!ret) + ret =put_user (0 , &infop->si_status); + } +/* avoid REGPARM breakage on x86: */ asmlinkage_protect (5 , ret, which, upid, infop, options, ru);return ret;kernel/sys_ni.c +1
-0@@ -163,6 +163,7 @@ cond_syscall(sys_ioprio_set);cond_syscall (sys_ioprio_get);/* New file descriptors */ +cond_syscall (sys_waitfd);cond_syscall (sys_signalfd);cond_syscall (sys_signalfd4);cond_syscall (compat_sys_signalfd);
Signature · 1 line
~Randy
Quoted 29 lines · Randy Dunlap
Quoted 27 lines · Casey Dahlin
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
--CJD
arch/x86/include/asm/unistd_32.h +1
-0@@ -338,6 +338,7 @@#define __NR_dup3 330#define __NR_pipe2 331#define __NR_inotify_init1 332 +#define __NR_waitfd 333#ifdef __KERNEL__
Quoted 17 lines
arch/x86/include/asm/unistd_64.h __SYSCALL(__NR_dup3, sys_dup3) · line 659
Quoted 13 lines
arch/x86/include/asm/unistd_64.h +2
-0@@ -653,6 +653,8 @@ __SYSCALL(__NR_dup3, sys_dup3) __SYSCALL(__NR_pipe2, sys_pipe2)#define __NR_inotify_init1 294 __SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_waitfd 295 +__SYSCALL (__NR_waitfd, sys_waitfd)Only for x86??
At the moment. I should have mentioned this earlier but I haven't made the syscall table entries for archs I don't test on. That will change once the rest of the change has settled out.
Quoted 54 lines
Quoted 54 lines
arch/x86/include/asm/unistd_64.h +0
-0#ifndef __NO_STUBSarch/x86/kernel/syscall_table_32.S +1
-0@@ -332,3 +332,4 @@ ENTRY(sys_call_table) .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 + .long sys_waitfdfs/Makefile +0
-0@@ -25,6 +25,7 @@ obj-$(CONFIG_INOTIFY_USER) += inotify_user.oobj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_ANON_INODES) += anon_inodes.o
obj-$(CONFIG_SIGNALFD) += signalfd.o+obj-$(CONFIG_WAITFD) += waitfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.oobj-$(CONFIG_AIO) += aio.o
fs/waitfd.c new file +25
-0@@ -0,0 +1,117 @@ +/* +* fs/waitfd.c +* +* Copyright (C) 2008 Red Hat, Casey Dahlin <[email protected]> +* +* Largely derived from fs/signalfd.c +*/ + +#include <linux/file.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/list.h> +#include <linux/anon_inodes.h> +#include <linux/syscalls.h> + +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru); + +struct waitfd_ctx { +int ops;
Quoted 10 lines
fs/waitfd.c line 29
Quoted 5 lines
fs/waitfd.c new file +4
-0+int which; +pid_t upid; +}; +Please use kernel coding style: use tabs to indent, not <lots-of-spaces>, and struct members, functions, etc., are indented by one tab stop minimum.
Damnit. This is a mailer artifact. This is the first time thunderbird has eaten a patch on me. I'll look in to it.
Quoted 57 lines
Quoted 45 lines
fs/waitfd.c new file +24
-0+static int waitfd_release (struct inode *inode,struct file *file) +{ +kfree (file->private_data); +return 0 ; +} + +static unsigned int waitfd_poll (struct file *file,poll_table *wait) +{ +struct waitfd_ctx *ctx = file->private_data; +long value; + +poll_wait (file, ¤t->signal->wait_chldexit, wait); + + value =do_waitid (ctx->which, ctx->upid,NULL , + ctx->ops |WNOHANG |WNOWAIT ,NULL ); +if (value >0 || value == -ECHILD ) +return POLLIN ; + +return 0 ; +} + +/* +* Returns a multiple of the size of a struct siginfo, or a negative +* error code. The "count" parameter must be at least sizeof(struct siginfo)
+ */
+staticssize_twaitfd_read(struct file *file, char__user*buf,size_tcount,fs/waitfd.c new file +16
-0+ loff_t *ppos) +{ +struct waitfd_ctx *ctx = file->private_data; +struct siginfo __user *info_addr = (struct siginfo *)buf; +int flags = ctx->ops; +ssize_t ret, total =0 ; + + count /=sizeof (struct siginfo ); +if (!count) +return -EINVAL ; + + do { + ret = do_waitid(ctx->which, ctx->upid, info_addr, flags, NULL); + if (ret == 0) + ret = -EAGAIN; + if (ret == -ECHILD)fs/waitfd.c line 81
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ret =0 ; +if (ret <=0 ) +break ; + + info_addr++; + total +=sizeof (struct siginfo ); + flags |=WNOHANG ; + }while (--count); + +return total ? total: ret;return total ? total : ret;
Quoted 3 lines
please.
I actually saw this used elsewhere in the kernel. Assumed I'd missed it in the style guidelines :)
Quoted 15 lines
Quoted 15 lines
fs/waitfd.c new file +15
-0+} + +static const struct file_operations waitfd_fops = { + .release = waitfd_release, + .poll = waitfd_poll, + .read = waitfd_read, +}; + +asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;
Quoted 10 lines
fs/waitfd.c line 101
Quoted 6 lines
fs/waitfd.c new file +5
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ;Use spaces around '|'.
Those 4 lines are copied almost exactly from kernel/exit.c. Is there motivation to keep them consistent?
Quoted 34 lines
Quoted 21 lines
fs/waitfd.c new file +16
-0+ + ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 ); +if (ufd <0 ) +kfree (ctx); + +return ufd; +}init/Kconfig +0
-0init/Kconfig config EPOLL · line 692
Quoted 10 lines
init/Kconfig +6
-0@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child statereceiving
Quoted 3 lines
Kconfighelp text should be indented by <tab><space><space>.
Likely more of the mailer eating the patch.
Quoted 131 lines
Quoted 131 lines
init/Kconfig +4
-0+ changes from a file descriptor. + + If unsure, say Y. +config SIGNALFD
bool "Enablesignalfd()system call" if EMBEDDED selectANON_INODESkernel/exit.c +14
-12@@ -1233,18 +1233,20 @@ static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid, int retval = rusagep ? getrusage(p, RUSAGE_BOTH, rusagep) : 0;put_task_struct (p); -if (!retval) - retval =put_user (SIGCHLD , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user ((short )why, &infop->si_code); -if (!retval) - retval =put_user (pid, &infop->si_pid); -if (!retval) - retval =put_user (uid, &infop->si_uid); -if (!retval) - retval =put_user (status, &infop->si_status); +if (infop) { +if (!retval) + retval =put_user (SIGCHLD , &infop->si_signo); +if (!retval) + retval =put_user (0 , &infop->si_errno); +if (!retval) + retval =put_user ((short )why, &infop->si_code); +if (!retval) + retval =put_user (pid, &infop->si_pid); +if (!retval) + retval =put_user (uid, &infop->si_uid); +if (!retval) + retval =put_user (status, &infop->si_status); + }if (!retval) retval = pid;return retval;kernel/exit.c +0
-0@@ -1727,35 +1729,12 @@ repeat:end:
current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit,&wait);kernel/exit.c +0
-23-if (infop) { -if (retval >0 ) - retval =0 ; -else { -/* -* For a WNOHANG return, clear out all the fields -* we would set so the user can easily tell the -* difference. -*/ -if (!retval) - retval =put_user (0 , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user (0 , &infop->si_code); -if (!retval) - retval =put_user (0 , &infop->si_pid); -if (!retval) - retval =put_user (0 , &infop->si_uid); -if (!retval) - retval =put_user (0 , &infop->si_status); - } - }return retval;}
kernel/exit.c +3
-3-asmlinkage long sys_waitid (int which,pid_t upid, -struct siginfo __user *infop,int options, -struct rusage __user *ru) +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru){ struct pid *pid = NULL; enum pid_type type;kernel/exit.c +33
-0@@ -1789,6 +1768,39 @@ asmlinkage long sys_waitid(int which, pid_t upid, ret =do_wait (type, pid, options, infop,NULL , ru);put_pid (pid); +return ret; +} + +asmlinkage long sys_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) +{ +long ret; + + ret =do_waitid (which, upid, infop, options, ru); + +if (ret >0 ) + ret =0 ; +else { +/* +* For a WNOHANG return, clear out all the fields +* we would set so the user can easily tell the +* difference. +*/ +if (!ret) + ret =put_user (0 , &infop->si_signo); +if (!ret) + ret =put_user (0 , &infop->si_errno); +if (!ret) + ret =put_user (0 , &infop->si_code); +if (!ret) + ret =put_user (0 , &infop->si_pid); +if (!ret) + ret =put_user (0 , &infop->si_uid); +if (!ret) + ret =put_user (0 , &infop->si_status); + } +/* avoid REGPARM breakage on x86: */ asmlinkage_protect (5 , ret, which, upid, infop, options, ru);return ret;kernel/sys_ni.c +0
-0@@ -163,6 +163,7 @@ cond_syscall(sys_ioprio_set);cond_syscall (sys_ioprio_get);
Quoted 6 lines
kernel/sys_ni.c cond_syscall(sys_ioprio_set); · line 169
Quoted 6 lines
kernel/sys_ni.c +1
-0/* New file descriptors */ +cond_syscall (sys_waitfd);cond_syscall (sys_signalfd);cond_syscall (sys_signalfd4);cond_syscall (compat_sys_signalfd);
Thanks for the review.
--CJD
Quoted 97 lines · Casey Dahlin
Quoted 29 lines · Randy Dunlap
Quoted 27 lines · Casey Dahlin
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
--CJD
arch/x86/include/asm/unistd_32.h +1
-0@@ -338,6 +338,7 @@#define __NR_dup3 330#define __NR_pipe2 331#define __NR_inotify_init1 332 +#define __NR_waitfd 333#ifdef __KERNEL__Quoted 17 lines
arch/x86/include/asm/unistd_64.h __SYSCALL(__NR_dup3, sys_dup3) · line 659
Quoted 13 lines
arch/x86/include/asm/unistd_64.h +2
-0@@ -653,6 +653,8 @@ __SYSCALL(__NR_dup3, sys_dup3) __SYSCALL(__NR_pipe2, sys_pipe2)#define __NR_inotify_init1 294 __SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_waitfd 295 +__SYSCALL (__NR_waitfd, sys_waitfd)Only for x86??
At the moment. I should have mentioned this earlier but I haven't made the syscall table entries for archs I don't test on. That will change once the rest of the change has settled out.
Quoted 33 lines
Quoted 33 lines
fs/waitfd.c new file +27
-0@@ -0,0 +1,117 @@ +/* +* fs/waitfd.c +* +* Copyright (C) 2008 Red Hat, Casey Dahlin <[email protected]> +* +* Largely derived from fs/signalfd.c +*/ + +#include <linux/file.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/list.h> +#include <linux/anon_inodes.h> +#include <linux/syscalls.h> + +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru); + +struct waitfd_ctx { +int ops; +int which; +pid_t upid;Quoted 10 lines
fs/waitfd.c line 29
Quoted 3 lines
fs/waitfd.c new file +2
-0+}; +Please use kernel coding style: use tabs to indent, not <lots-of-spaces>,
and struct members, functions, etc., are indented by one tab stop minimum.
Quoted 2 lines
Damnit. This is a mailer artifact. This is the first time thunderbird has eaten a patch on me. I'll look in to it.
OK.
You can see if Documentation/email-clients.txt helps you any.
Quoted 27 lines
Quoted 16 lines
Quoted 16 lines
fs/waitfd.c new file +9
-0+} + +static const struct file_operations waitfd_fops = { + .release = waitfd_release, + .poll = waitfd_poll, + .read = waitfd_read, +}; + +asmlinkage long sys_waitfd(int which, pid_t upid, int options, intunused)
fs/waitfd.c new file +6
-0+{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;Quoted 10 lines
fs/waitfd.c line 101
Quoted 6 lines
fs/waitfd.c new file +5
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ;Use spaces around '|'.
Quoted 2 lines
Those 4 lines are copied almost exactly from
kernel/exit.c. Is there motivation to keep them consistent?
I would say no.
Signature · 1 line
~Randy
Quoted 11 lines · Casey Dahlin
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
Can you please provide a manpage with the exact proposed semantics of the interface.
Without that it's very hard to evaluate if the interface makes sense or not.
In general a single paragraph is not enough to justify a new system call.
-Andi
Signature · 1 line
[email protected]
RFC PATCH v2 1/1 waitfd
In reply to Casey DahlinSame as last time, but the formatting should be right now.
Original description:
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
waitfd provides a file descriptor out of which may be read a series of siginfo_t objects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.
Patch is against v2.6.28
arch/x86/include/asm/unistd_32.h +1 -0
@@ -338,6 +338,7 @@#define __NR_dup3 330#define __NR_pipe2 331#define __NR_inotify_init1 332 +#define __NR_waitfd 333#ifdef __KERNEL__
arch/x86/include/asm/unistd_64.h +2 -0
@@ -653,6 +653,8 @@ __SYSCALL(__NR_dup3, sys_dup3) __SYSCALL(__NR_pipe2, sys_pipe2)#define __NR_inotify_init1 294 __SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_waitfd 295 +__SYSCALL(__NR_waitfd, sys_waitfd)#ifndef __NO_STUBS
arch/x86/kernel/syscall_table_32.S +1 -0
@@ -332,3 +332,4 @@ ENTRY(sys_call_table) .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 + .long sys_waitfd
fs/Makefile +1 -0
@@ -25,6 +25,7 @@ obj-$(CONFIG_INOTIFY_USER) += inotify_user.o obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_ANON_INODES) += anon_inodes.o obj-$(CONFIG_SIGNALFD) += signalfd.o +obj-$(CONFIG_WAITFD) += waitfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o obj-$(CONFIG_AIO) += aio.o
fs/waitfd.c new file +117 -0
117 lines not on this page · raw message
init/Kconfig +10 -0
@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child state + changes from a file descriptor. + + If unsure, say Y. + config SIGNALFD bool "Enable signalfd() system call" if EMBEDDED select ANON_INODES
kernel/exit.c +50 -38
109 lines not on this page · raw message
kernel/sys_ni.c +1 -0
@@ -163,6 +163,7 @@ cond_syscall(sys_ioprio_set);cond_syscall (sys_ioprio_get);/* New file descriptors */ +cond_syscall (sys_waitfd);cond_syscall (sys_signalfd);cond_syscall (sys_signalfd4);cond_syscall (compat_sys_signalfd);
(Cc:-ed a few more folks who might be interested in this)
Quoted 7 lines · Casey Dahlin
Original description:
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
Quoted 6 lines
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
The principle looks sound and acceptable - to complete the epoll mechanism to all event sources in the system. There's a few small (mostly stylistic) details though:
fs/waitfd.c line 22
Quoted 10 lines
index 0000000..0155a83
fs/waitfd.c new file +6
-0+#include <linux/anon_inodes.h> +#include <linux/syscalls.h> + +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru);
please move this prototype into sched.h.
fs/waitfd.c line 28
Quoted 5 lines
fs/waitfd.c new file +5
-0+struct waitfd_ctx { +int ops; +int which; +pid_t upid; +};
please align structure fields vertically, similarly to how you aligned other definitions in your patch. Something like:
fs/waitfd.c line 28
Quoted 5 lines
fs/waitfd.c new file +5
-0+struct waitfd_ctx { +int ops; +int which; +pid_t upid; +};
(otherwise it looks all a bit too crammed together)
Quoted 42 lines
fs/waitfd.c new file +42
-0+static int waitfd_release (struct inode *inode,struct file *file) +{ +kfree (file->private_data); +return 0 ; +} + +static unsigned int waitfd_poll (struct file *file,poll_table *wait) +{ +struct waitfd_ctx *ctx = file->private_data; +long value; + +poll_wait (file, ¤t->signal->wait_chldexit, wait); + + value =do_waitid (ctx->which, ctx->upid,NULL , + ctx->ops |WNOHANG |WNOWAIT ,NULL ); +if (value >0 || value == -ECHILD ) +return POLLIN ; + +return 0 ; +} + +/* +* Returns a multiple of the size of a struct siginfo, or a negative +* error code. The "count" parameter must be at least sizeof(struct siginfo) +*/ +static ssize_t waitfd_read (struct file *file,char __user *buf,size_t count, +loff_t *ppos) +{ +struct waitfd_ctx *ctx = file->private_data; +struct siginfo __user *info_addr = (struct siginfo *)buf; +int flags = ctx->ops; +ssize_t ret, total =0 ; + + count /=sizeof (struct siginfo ); +if (!count) +return -EINVAL ; + + do { + ret = do_waitid(ctx->which, ctx->upid, info_addr, flags, NULL); + if (ret == 0) + ret = -EAGAIN; + if (ret == -ECHILD)
fs/waitfd.c line 81
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ret =0 ; +if (ret <=0 ) +break ; + + info_addr++; + total +=sizeof (struct siginfo ); + flags |=WNOHANG ; + }while (--count); + +return total ? total: ret;
please use symmetric spacing, i.e.:
Quoted 7 lines
fs/waitfd.c new file +6
-0+return total ? total : ret; +} + +static const struct file_operations waitfd_fops = { + .release = waitfd_release, + .poll = waitfd_poll,
fs/waitfd.c line 96
Quoted 10 lines
fs/waitfd.c new file +10
-0+ .read = waitfd_read, +}; + +asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;
looks a bit silly ...
Quoted 26 lines
fs/waitfd.c new file +21
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ; + + ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 ); +if (ufd <0 ) +kfree (ctx); + +return ufd; +}init/Kconfig +0
-0
init/Kconfig config EPOLL · line 691
Quoted 9 lines
init/Kconfig +6
-0@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child state
typo.
Quoted 108 lines
init/Kconfig +4
-0+ changes from a file descriptor. + + If unsure, say Y. + config SIGNALFD bool "Enable signalfd() system call" if EMBEDDED select ANON_INODESkernel/exit.c +38
-38@@ -1233,18 +1233,20 @@ static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,int retval = rusagep ?getrusage (p,RUSAGE_BOTH , rusagep) :0 ;put_task_struct (p); -if (!retval) - retval =put_user (SIGCHLD , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user ((short )why, &infop->si_code); -if (!retval) - retval =put_user (pid, &infop->si_pid); -if (!retval) - retval =put_user (uid, &infop->si_uid); -if (!retval) - retval =put_user (status, &infop->si_status); +if (infop) { +if (!retval) + retval =put_user (SIGCHLD , &infop->si_signo); +if (!retval) + retval =put_user (0 , &infop->si_errno); +if (!retval) + retval =put_user ((short )why, &infop->si_code); +if (!retval) + retval =put_user (pid, &infop->si_pid); +if (!retval) + retval =put_user (uid, &infop->si_uid); +if (!retval) + retval =put_user (status, &infop->si_status); + }if (!retval) retval = pid;return retval;@@ -1727,35 +1729,12 @@ repeat: end: current->state =TASK_RUNNING ;remove_wait_queue (¤t->signal->wait_chldexit,&wait); -if (infop) { -if (retval >0 ) - retval =0 ; -else { -/* -* For a WNOHANG return, clear out all the fields -* we would set so the user can easily tell the -* difference. -*/ -if (!retval) - retval =put_user (0 , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user (0 , &infop->si_code); -if (!retval) - retval =put_user (0 , &infop->si_pid); -if (!retval) - retval =put_user (0 , &infop->si_uid); -if (!retval) - retval =put_user (0 , &infop->si_status); - } - }return retval; } -asmlinkage long sys_waitid (int which,pid_t upid, -struct siginfo __user *infop,int options, -struct rusage __user *ru) +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) {struct pid *pid =NULL ;enum pid_type type;@@ -1789,6 +1768,39 @@ asmlinkage long sys_waitid(int which, pid_t upid, ret =do_wait (type, pid, options, infop,NULL , ru);put_pid (pid); +return ret; +} + +asmlinkage long sys_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) +{ +long ret; + + ret =do_waitid (which, upid, infop, options, ru); + +if (ret >0 ) + ret =0 ; +else { +/* +* For a WNOHANG return, clear out all the fields +* we would set so the user can easily tell the +* difference. +*/ +if (!ret) + ret =put_user (0 , &infop->si_signo);
kernel/exit.c asmlinkage long sys_waitid(int which, pid_t upid, · line 1801
Quoted 10 lines
kernel/exit.c +10
-0+if (!ret) + ret =put_user (0 , &infop->si_errno); +if (!ret) + ret =put_user (0 , &infop->si_code); +if (!ret) + ret =put_user (0 , &infop->si_pid); +if (!ret) + ret =put_user (0 , &infop->si_uid); +if (!ret) + ret =put_user (0 , &infop->si_status);
even if this just moves existing code around, if we touch this, it would be far cleaner (and faster as well) to do what other bits of the signal code do:
kernel/exit.c asmlinkage long sys_waitid(int which, pid_t upid,
Quoted 6 lines
kernel/exit.c +6
-0+ ret =put_user (0 , &infop->si_signo); + ret |=put_user (0 , &infop->si_errno); + ret |=put_user (0 , &infop->si_code); + ret |=put_user (0 , &infop->si_pid); + ret |=put_user (0 , &infop->si_uid); + ret |=put_user (0 , &infop->si_status);
Since put_user() can only return -EFAULT or zero.
(same for wait_noreap_copyout())
Ingo
Quoted 26 lines · Ingo Molnar
(Cc:-ed a few more folks who might be interested in this)
Quoted 7 lines · Casey Dahlin
Original description:
Linux now exposes signals, timers, and events via file descriptors through signalfd, timerfd, and eventfd. This means programmers can use a single select/[e]poll call to monitor all change in their program. This patch aims to expose child death via the same mechanism.
Quoted 6 lines
waitfd provides a file descriptor out of which may be read a series of
siginfo_tobjects describing child death. A child process is reaped as soon as its information is read. This means child monitoring too can be performed with that same poll call.Patch is against v2.6.28
The principle looks sound and acceptable - to complete the epoll mechanism to all event sources in the system. There's a few small (mostly stylistic) details though:
Quoted 3 lines
index 0000000..0155a83
fs/waitfd.c new file +0
-0
Quoted 9 lines
fs/waitfd.c line 22
Quoted 6 lines
fs/waitfd.c new file +6
-0+#include <linux/anon_inodes.h> +#include <linux/syscalls.h> + +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru);please move this prototype into
sched.h.
Sure thing. Knew it shouldn't be here, just didn't know where it went. I'm surprised you're the first to have complained :)
Quoted 83 lines
fs/waitfd.c line 28
Quoted 5 lines
fs/waitfd.c new file +5
-0+struct waitfd_ctx { +int ops; +int which; +pid_t upid; +};please align structure fields vertically, similarly to how you aligned other definitions in your patch. Something like:
fs/waitfd.c line 28
Quoted 5 lines
fs/waitfd.c new file +5
-0+struct waitfd_ctx { +int ops; +int which; +pid_t upid; +};(otherwise it looks all a bit too crammed together)
Quoted 42 lines
fs/waitfd.c new file +42
-0+static int waitfd_release (struct inode *inode,struct file *file) +{ +kfree (file->private_data); +return 0 ; +} + +static unsigned int waitfd_poll (struct file *file,poll_table *wait) +{ +struct waitfd_ctx *ctx = file->private_data; +long value; + +poll_wait (file, ¤t->signal->wait_chldexit, wait); + + value =do_waitid (ctx->which, ctx->upid,NULL , + ctx->ops |WNOHANG |WNOWAIT ,NULL ); +if (value >0 || value == -ECHILD ) +return POLLIN ; + +return 0 ; +} + +/* +* Returns a multiple of the size of a struct siginfo, or a negative +* error code. The "count" parameter must be at least sizeof(struct siginfo) +*/ +static ssize_t waitfd_read (struct file *file,char __user *buf,size_t count, +loff_t *ppos) +{ +struct waitfd_ctx *ctx = file->private_data; +struct siginfo __user *info_addr = (struct siginfo *)buf; +int flags = ctx->ops; +ssize_t ret, total =0 ; + + count /=sizeof (struct siginfo ); +if (!count) +return -EINVAL ; + + do { + ret = do_waitid(ctx->which, ctx->upid, info_addr, flags, NULL); + if (ret == 0) + ret = -EAGAIN; + if (ret == -ECHILD)fs/waitfd.c line 81
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ret =0 ; +if (ret <=0 ) +break ; + + info_addr++; + total +=sizeof (struct siginfo ); + flags |=WNOHANG ; + }while (--count); + +return total ? total: ret;please use symmetric spacing, i.e.:
Quoted 11 lines
fs/waitfd.c new file +10
-0+return total ? total : ret; +} + +static const struct file_operations waitfd_fops = { + .release = waitfd_release, + .poll = waitfd_poll, + .read = waitfd_read, +}; + +asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused)
Quoted 8 lines
fs/waitfd.c line 96
Quoted 6 lines
fs/waitfd.c new file +6
-0+{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;looks a bit silly ...
Do you mean the principle of having an unused argument around for future use or the cast to void? The cast to void is there to suppress the "Waning: unused argument" messages and make gcc happy.
Quoted 170 lines
Quoted 26 lines
fs/waitfd.c new file +21
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ; + + ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 ); +if (ufd <0 ) +kfree (ctx); + +return ufd; +}init/Kconfig +0
-0init/Kconfig config EPOLL · line 691
Quoted 9 lines
init/Kconfig +6
-0@@ -683,6 +683,16 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config WAITFD + bool "Enable waitfd() system call" if EMBEDDED + select ANON_INODES + default y + help + Enable the waitfd() system call that allows receving child statetypo.
Quoted 108 lines
init/Kconfig +4
-0+ changes from a file descriptor. + + If unsure, say Y. + config SIGNALFD bool "Enable signalfd() system call" if EMBEDDED select ANON_INODESkernel/exit.c +38
-38@@ -1233,18 +1233,20 @@ static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,int retval = rusagep ?getrusage (p,RUSAGE_BOTH , rusagep) :0 ;put_task_struct (p); -if (!retval) - retval =put_user (SIGCHLD , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user ((short )why, &infop->si_code); -if (!retval) - retval =put_user (pid, &infop->si_pid); -if (!retval) - retval =put_user (uid, &infop->si_uid); -if (!retval) - retval =put_user (status, &infop->si_status); +if (infop) { +if (!retval) + retval =put_user (SIGCHLD , &infop->si_signo); +if (!retval) + retval =put_user (0 , &infop->si_errno); +if (!retval) + retval =put_user ((short )why, &infop->si_code); +if (!retval) + retval =put_user (pid, &infop->si_pid); +if (!retval) + retval =put_user (uid, &infop->si_uid); +if (!retval) + retval =put_user (status, &infop->si_status); + }if (!retval) retval = pid;return retval;@@ -1727,35 +1729,12 @@ repeat: end: current->state =TASK_RUNNING ;remove_wait_queue (¤t->signal->wait_chldexit,&wait); -if (infop) { -if (retval >0 ) - retval =0 ; -else { -/* -* For a WNOHANG return, clear out all the fields -* we would set so the user can easily tell the -* difference. -*/ -if (!retval) - retval =put_user (0 , &infop->si_signo); -if (!retval) - retval =put_user (0 , &infop->si_errno); -if (!retval) - retval =put_user (0 , &infop->si_code); -if (!retval) - retval =put_user (0 , &infop->si_pid); -if (!retval) - retval =put_user (0 , &infop->si_uid); -if (!retval) - retval =put_user (0 , &infop->si_status); - } - }return retval; } -asmlinkage long sys_waitid (int which,pid_t upid, -struct siginfo __user *infop,int options, -struct rusage __user *ru) +long do_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) {struct pid *pid =NULL ;enum pid_type type;@@ -1789,6 +1768,39 @@ asmlinkage long sys_waitid(int which, pid_t upid, ret =do_wait (type, pid, options, infop,NULL , ru);put_pid (pid); +return ret; +} + +asmlinkage long sys_waitid (int which,pid_t upid, +struct siginfo __user *infop,int options, +struct rusage __user *ru) +{ +long ret; + + ret =do_waitid (which, upid, infop, options, ru); + +if (ret >0 ) + ret =0 ; +else { +/* +* For a WNOHANG return, clear out all the fields +* we would set so the user can easily tell the +* difference. +*/ +if (!ret) + ret =put_user (0 , &infop->si_signo);kernel/exit.c asmlinkage long sys_waitid(int which, pid_t upid, · line 1801
Quoted 10 lines
kernel/exit.c +10
-0+if (!ret) + ret =put_user (0 , &infop->si_errno); +if (!ret) + ret =put_user (0 , &infop->si_code); +if (!ret) + ret =put_user (0 , &infop->si_pid); +if (!ret) + ret =put_user (0 , &infop->si_uid); +if (!ret) + ret =put_user (0 , &infop->si_status);even if this just moves existing code around, if we touch this, it would be far cleaner (and faster as well) to do what other bits of the signal code do:
kernel/exit.c asmlinkage long sys_waitid(int which, pid_t upid,
Quoted 6 lines
kernel/exit.c +6
-0+ ret =put_user (0 , &infop->si_signo); + ret |=put_user (0 , &infop->si_errno); + ret |=put_user (0 , &infop->si_code); + ret |=put_user (0 , &infop->si_pid); + ret |=put_user (0 , &infop->si_uid); + ret |=put_user (0 , &infop->si_status);Since
put_user()can only return-EFAULTor zero.
Quoted 2 lines
(same for
wait_noreap_copyout())
I'll change this in both of those places. I'm hoping to find time at some point to submit some general cleanups to do_wait. There's a lot about it I don't like.
--CJD
Quoted 11 lines · Casey Dahlin
Quoted 9 lines
fs/waitfd.c line 96
Quoted 7 lines
fs/waitfd.c new file +7
-0+asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;looks a bit silly ...
Quoted 3 lines
Do you mean the principle of having an unused argument around for future use or the cast to void? The cast to void is there to suppress the "Waning: unused argument" messages and make gcc happy.
gcc will not warn about unused function arguments - only about unused local variables. The 'unused' argument should either be removed altogether, or replaced with a properly named parameter and a check returning -ENOSYS if the argument is not zero (or something like that).
(It's generally better to keep such syscalls extensible via such trivial means, if there's a remote chance for ever needing to extend that syscall.)
Ingo
Quoted 15 lines · Ingo Molnar
(Cc:-ed a few more folks who might be interested in this)
fs/waitfd.c line 96
Quoted 7 lines · Casey Dahlin
fs/waitfd.c new file +7
-0+asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;looks a bit silly ...
Quoted 16 lines
Quoted 6 lines
fs/waitfd.c new file +6
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ; +fs/waitfd.c line 112
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 );
minor nit...
Please note that unlike other sys_...fd() syscalls, sys_waitfd() doesn't allow to pass O_CLOEXEC. Looks like we need a separate "flags" argument...
Also, ioctl(FIONBIO) or fcntl(O_NONBLOCK) have no effect on waitfd, not very good.
I'd suggest to remove WNOHANG from waitfd_ctx->ops and treat (->f_flags & O_NONBLOCK) as WNOHANG.
(can't resist, ->ops is not the best name ;)
Oleg.
Quoted 39 lines · Oleg Nesterov
Quoted 15 lines · Ingo Molnar
(Cc:-ed a few more folks who might be interested in this)
fs/waitfd.c line 96
Quoted 7 lines · Casey Dahlin
fs/waitfd.c new file +7
-0+asmlinkage long sys_waitfd (int which,pid_t upid,int options,int unused) +{ +int ufd; +struct waitfd_ctx *ctx; + +/* Just to make sure we don't end up with a sys_waitfd4 */ + (void )unused;looks a bit silly ...
Quoted 16 lines
Quoted 6 lines
fs/waitfd.c new file +6
-0+ +if (options & ~(WNOHANG |WEXITED |WSTOPPED |WCONTINUED )) +return -EINVAL ; +if (!(options & (WEXITED |WSTOPPED |WCONTINUED ))) +return -EINVAL ; +fs/waitfd.c line 112
Quoted 10 lines
fs/waitfd.c new file +10
-0+ ctx =kmalloc (sizeof (*ctx),GFP_KERNEL ); +if (!ctx) +return -ENOMEM ; + + ctx->ops = options; + ctx->upid = upid; + ctx->which = which; + + ufd =anon_inode_getfd ("[waitfd]" , &waitfd_fops, ctx, + (options &WNOHANG ) ?O_NONBLOCK :0 );minor nit...
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...
Quoted 7 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.(can't resist, ->ops is not the best name ;)
yeah, ->ops should be ->options. The name ->ops is generally use for method vectors and so.
Ingo
Quoted 5 lines · Oleg Nesterov
minor nit...
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...
I said this even during the first post. Not a minor nit unless we want to have a sys_waitfd2 soon after.
Quoted 2 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.
Ack.
Quoted 2 lines
I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.
Ack.
Quoted 1 line
(can't resist, ->ops is not the best name ;)
Ack. "flags" looks a pretty nice shot at it.
- Davide
Quoted 11 lines · Davide Libenzi
Quoted 5 lines · Oleg Nesterov
minor nit...
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...I said this even during the first post. Not a minor nit unless we want to have a
sys_waitfd2soon after.
I don't think I properly understood the complaint last time you made it.
Fix on the way
--CJD
Quoted 4 lines · Oleg Nesterov
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...
Quoted 8 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.(can't resist, ->ops is not the best name ;)
Definitely agree here, waitfd() doesn't need WNOHANG - we already have ONONBLOCK.
That also solves one of the strangest behaves of waitid when you use WNOHANG (it returns zero and you have to check whether it changed the struct), now you just read() - if no child you get EAGAIN, if a child you read a struct.
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 19 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...Quoted 9 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.(can't resist, ->ops is not the best name ;)
Definitely agree here,
waitfd()doesn't need WNOHANG - we already have ONONBLOCK.
Quoted 7 lines
That also solves one of the strangest behaves of waitid when you use WNOHANG (it returns zero and you have to check whether it changed the struct), now you just
read()- if no child you get EAGAIN, if a child you read a struct.Scott
From the perspective of waitfd, the only difference between WNOHANG and O_NONBLOCK is which argument you put the flags in. The API should only support one or the other, but internally they would imply the same thing.
--CJD
Quoted 4 lines · Casey Dahlin
From the perspective of waitfd, the only difference between WNOHANG and
O_NONBLOCKis which argument you put the flags in. The API should only support one or the other, but internally they would imply the same thing.
Well, you get O_NONBLOCK for free by having a file descriptor; and you can't turn off people trying to turn it on/off with fcntl() - so you may as well just use that, no? :-)
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 8 lines · Scott James Remnant
Quoted 5 lines · Casey Dahlin
From the perspective of waitfd, the only difference between WNOHANG and
O_NONBLOCKis which argument you put the flags in. The API should only support one or the other, but internally they would imply the same thing.
Quoted 6 lines
Well, you get
O_NONBLOCKfor free by having a file descriptor; and you can't turn off people trying to turn it on/off withfcntl()- so you may as well just use that, no? :-)Scott
Its purely an api question. We could easily take the WNOHANG flag and just unset it when we get it and set O_NONBLOCK instead. We need O_CLOEXEC anyway though, and the only reason to do it would be to get rid of the O_ options and take only one type of flag (that and just a little more waitid consistency).
--CJD
Quoted 20 lines · Casey Dahlin
Quoted 18 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...Quoted 9 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.(can't resist, ->ops is not the best name ;)
Definitely agree here,
waitfd()doesn't need WNOHANG - we already have ONONBLOCK.
Quoted 7 lines
Quoted 5 lines
That also solves one of the strangest behaves of waitid when you use WNOHANG (it returns zero and you have to check whether it changed the struct), now you just
read()- if no child you get EAGAIN, if a child you read a struct.From the perspective of waitfd, the only difference between WNOHANG and
O_NONBLOCKis which argument you put the flags in.
No. Please see the note about ioctl/fcntl above.
Oleg.
Quoted 30 lines · Oleg Nesterov
Quoted 28 lines · Casey Dahlin
Quoted 26 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
Please note that unlike other sys_...fd() syscalls,
sys_waitfd()doesn't allow to passO_CLOEXEC. Looks like we need a separate "flags" argument...Quoted 10 lines
Also, ioctl(FIONBIO) or
fcntl(O_NONBLOCK)have no effect on waitfd, not very good.I'd suggest to remove WNOHANG from
waitfd_ctx->opsand treat (->f_flags &O_NONBLOCK) as WNOHANG.(can't resist, ->ops is not the best name ;)
Definitely agree here,
waitfd()doesn't need WNOHANG - we already have ONONBLOCK.That also solves one of the strangest behaves of waitid when you use WNOHANG (it returns zero and you have to check whether it changed the struct), now you just
read()- if no child you get EAGAIN, if a child you read a struct.
Quoted 8 lines
Quoted 3 lines
From the perspective of waitfd, the only difference between WNOHANG and
O_NONBLOCKis which argument you put the flags in.No. Please see the note about ioctl/fcntl above.
Oleg.
Yes but the actual waitfd call could simply set O_NONBLOCK on the descriptor when it receive WNOHANG in the flags, and read the descriptor flags going forward.
--CJD
Quoted 5 lines · Casey Dahlin
Quoted 3 lines · Oleg Nesterov
Quoted 2 lines
From the perspective of waitfd, the only difference between WNOHANG and
O_NONBLOCKis which argument you put the flags in.
Quoted 7 lines
Quoted 4 lines
No. Please see the note about ioctl/fcntl above.
Oleg.
Yes but the actual waitfd call could simply set
O_NONBLOCKon the descriptor when it receive WNOHANG in the flags, and read the descriptor flags going forward.
Ah, I misunderstood your message as if we shouldn't check f_flags at all.
Yes sure.
Oleg.
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's exit_signal to a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.
Thanks,
Roland
Quoted 7 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.
hm, i think it's cleaner conceptually than trying to wrap this into signalfd. Since we already have:
#define __NR_signalfd 321 #define __NR_timerfd_create 322 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327
is one more really such an issue?
Ingo
Quoted 14 lines · Ingo Molnar
Quoted 7 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.hm, i think it's cleaner conceptually than trying to wrap this into signalfd. Since we already have:
Quoted 7 lines
#define __NR_signalfd 321 #define __NR_timerfd_create 322 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327is one more really such an issue?
And what did eventfd do to you? :)
I partially agree with Roland (and I stated this during Casey's first post), this can be achieved in a not too troublesome way already. A new dedicated interface is easier for the challenged userspace coder, but I dunno if it's worth the new code (although it does have little footprint). Both ways are fine from my POV.
- Davide
Quoted 22 lines · Davide Libenzi
Quoted 20 lines · Ingo Molnar
Quoted 7 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.hm, i think it's cleaner conceptually than trying to wrap this into signalfd. Since we already have:
#define __NR_signalfd 321 #define __NR_timerfd_create 322 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327
Quoted 3 lines
Quoted 1 line
is one more really such an issue?
And what did eventfd do to you? :)
:)
Quoted 5 lines
I partially agree with Roland (and I stated this during Casey's first post), this can be achieved in a not too troublesome way already. A new dedicated interface is easier for the challenged userspace coder, but I dunno if it's worth the new code (although it does have little footprint). Both ways are fine from my POV.
i think we should be defensive and generous and do a clean separate syscall for this - we have a pretty bad track record in syscall interface cleanliness, today's borderline hack is tomorrow's limitation causing headaches. We never know what that extra flexibility that will buy us in the future.
Ingo
Quoted 5 lines · Roland McGrath
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces. I.e., it's not necessary to restrict the use to parents. Any process with the same UID should be allowed to call waitfd. This would allow some new user cases.
Quoted 10 lines · Ulrich Drepper
Quoted 5 lines · Roland McGrath
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
yes, it doesn't eat the notification (SIGCHLD), but it reaps a zombie, clears ->exit_code TASK_STOPPED/TASK_TRACED tasks, clears SIGNAL_STOP_CONTINUED.
Quoted 3 lines
I.e., it's not necessary to restrict the use to parents. Any process with the same UID should be allowed to call waitfd. This would allow some new user cases.
I don't see how it is possible to implement this...
The parent can sleep on ->wait_chldexit and it will be notified, but how can we wait for the unrelated process with the same UID ?
Even if sys_waitfd() uses P_PID, we can't use task->parent->signal->wait_chldexit, task->parent can exit before task exits.
Or I misunderstood you?
Oleg.
Quoted 2 lines
Quoted 2 lines
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
waitid can be used that way with WNOWAIT.
Quoted 4 lines · Roland McGrath
Quoted 2 lines
Quoted 2 lines
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
waitid can be used that way with WNOWAIT.
Yes, but waitfd does not have this flag. The reason being waitfd just calls waitid internally, and there is no guarantee (afaik) that calling waitid with WNOWAIT multiple times in succession will yield different results each time. This breaks the streaming behavior of the descriptor.
--CJD
Quoted 11 lines · Casey Dahlin
Quoted 4 lines · Roland McGrath
Quoted 2 lines
Quoted 2 lines
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
waitid can be used that way with WNOWAIT.
Yes, but waitfd does not have this flag. The reason being waitfd just calls waitid internally, and there is no guarantee (afaik) that calling waitid with WNOWAIT multiple times in succession will yield different results each time. This breaks the streaming behavior of the descriptor.
Yes, do_wait(WNOWAIT) will return the same result on each call. Until the next child dies, and this child is closer to the head of ->children list.
But the reason is not that waitfd just calls waitid() internally. Let's suppose we add another helper (or waitfd_read() can do this by hand) so that read(waitfd_with_WNOWAIT_flag) correctly returns the info about all childs. Now, what should the next read() do?
Btw. It is not that I am trying to argue against sys_waitfd(), but do you have the "real life" example when it can be useful? Yes, poll(). But we have signalfd. SIGCHLD is not rt signal, but afaics this is not the problem actually. Just curious.
Oleg.
Quoted 5 lines · Oleg Nesterov
Btw. It is not that I am trying to argue against
sys_waitfd(), but do you have the "real life" example when it can be useful? Yes,poll(). But we have signalfd. SIGCHLD is not rt signal, but afaics this is not the problem actually. Just curious.
signalfd() can't currently be made to work in the way you describe.
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 9 lines · Scott James Remnant
Quoted 5 lines · Oleg Nesterov
Btw. It is not that I am trying to argue against
sys_waitfd(), but do you have the "real life" example when it can be useful? Yes,poll(). But we have signalfd. SIGCHLD is not rt signal, but afaics this is not the problem actually. Just curious.
signalfd()can't currently be made to work in the way you describe.
Hmm. Could you clarify?
I am not sure we are talking about the same thing, but afaics poll() + signalfd can work to (say) reap the childs. Actually, ppoll() alone is enough.
No?
Oleg.
Quoted 4 lines · Oleg Nesterov
I am not sure we are talking about the same thing, but afaics
poll()+ signalfd can work to (say) reap the childs. Actually,ppoll()alone is enough.
Last time I checked, ppoll() was not actually implemented across all architectures in a manner that solved the race it was intended to solve.
I'd be delighted to learn that this had been fixed? :-)
Soctt
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 10 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
I am not sure we are talking about the same thing, but afaics
poll()+ signalfd can work to (say) reap the childs. Actually,ppoll()alone is enough.Last time I checked,
ppoll()was not actually implemented across all architectures in a manner that solved the race it was intended to solve.
As I said, this is imho unfair. But I mentioned ppol() "just in case".
My questiong was why do you think that "signalfd() can't currently be made to work in the way you describe". You have dropped this part to change the topic?
Oleg.
Quoted 12 lines · Oleg Nesterov
Quoted 10 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
I am not sure we are talking about the same thing, but afaics
poll()+ signalfd can work to (say) reap the childs. Actually,ppoll()alone is enough.Last time I checked,
ppoll()was not actually implemented across all architectures in a manner that solved the race it was intended to solve.
Quoted 6 lines
As I said, this is imho unfair. But I mentioned
ppol()"just in case".My questiong was why do you think that "
signalfd()can't currently be made to work in the way you describe". You have dropped this part to change the topic?
Sorry, I may not be following LKML etiquette correctly. These couple of recent threads (other than some bugs I found in wait last year) are my first real attempt to participate here.
I wasn't intending to "change the topic" or dropping the parts about changing signalfd() to somehow sweet it under the carpet.
Rather than posting repeatedly across the thread, I tried to consolidate my responses into the other post you've replied to.
You made an interesting point about ppoll here, so I only responded to that to find out whether the situation of that syscall had been improved.
Not so much changing the topic, but asking a side-bar question ;)
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 4 lines · Casey Dahlin
Quoted 3 lines · Roland McGrath
Quoted 2 lines
Quoted 2 lines
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
Quoted 8 lines
Quoted 1 line
waitid can be used that way with WNOWAIT.
Yes, but waitfd does not have this flag. The reason being waitfd just calls waitid internally, and there is no guarantee (afaik) that calling waitid with WNOWAIT multiple times in succession will yield different results each time. This breaks the streaming behavior of the descriptor.
This would definitely be a Nice To Have though!
Being able to use waitid() on another process of the same uid, with WNOHANG, in a streaming fashion would be a *very* cool thing.
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 17 lines · Scott James Remnant
Quoted 5 lines · Casey Dahlin
Quoted 3 lines · Roland McGrath
Quoted 3 lines
Quoted 3 lines
Since waitfd shouldn't consume the child termination notification waitfd should be more widely usable than the wait*() interfaces.
Quoted 9 lines
Quoted 2 lines
waitid can be used that way with WNOWAIT.
Yes, but waitfd does not have this flag. The reason being waitfd just calls waitid internally, and there is no guarantee (afaik) that calling waitid with WNOWAIT multiple times in succession will yield different results each time. This breaks the streaming behavior of the descriptor.
Quoted 7 lines
This would definitely be a Nice To Have though!
Being able to use
waitid()on another process of the same uid, with WNOHANG, in a streaming fashion would be a *very* cool thing.Scott
That could be done in a separate patch for waitid itself, and its a simple change to propagate it through waitfd (just one less EINVAL case). In terms of waitfd we'd be supersetting the interface, so its no big deal to add. In terms of waitid I'd have to check and make sure that we wouldn't be breaking compliance by allowing it to return something different on every call (this would likely affect cases where waitid(WNOWAIT) was used even in a normal fashion as well).
--CJD
Quoted 1 line · Roland McGrath
New syscall should have gone to linux-api, I think.
Yes, precisely. This requirement has been documented in SubmittingPatches for several months now. More details here:
http://thread.gmane.org/gmane.linux.ltp/5658
Casey, *please* don't submit a patch for a system call without also providing a test program, and some attempt at userspace documentation.
(Andi already pointed this out. From my POV, I don't need you to
write a full blown man page -- if you send me the text, I'll do the *roff stuff. But that text should accompany the patch that implements the syscall.)
Cheers,
Michael
Quoted 16 lines
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.Thanks,
Roland
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Found a documentation bug? http://www.kernel.org/doc/man-pages/reporting_bugs.html
Quoted 8 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.
Doesn't that also change the wait() options you need as well?
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 8 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.
This wouldn't help the init daemon case:
- the exit_signal is set on the child, not on the parent.
While the init daemon could clone() every new process and set exit_signal, this would not be set for processes reparented to init.
Even if we had a new syscall to change the exit_signal of a given process, *and* had the init reparent notification patch, this still wouldn't be sufficient; you'd have a race condition between the time you were notified of the reparent, and the time you set exit_signal, in which the child could die.
Since exit_signal is always reset to SIGCHLD before reparenting, this could be done by resetting it to a different signal; but at this point we're getting into a rather twisty method full of traps.
- exit_signal is reset to SIGCHLD on exec().
Pretty much a plan-killer ;)
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 24 lines · Scott James Remnant
Quoted 8 lines · Roland McGrath
New syscall should have gone to linux-api, I think.
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.This wouldn't help the init daemon case:
- the
exit_signalis set on the child, not on the parent.While the init daemon could
clone()every new process and setexit_signal, this would not be set for processes reparented to init.Even if we had a new syscall to change the
exit_signalof a given process, *and* had the init reparent notification patch, this still wouldn't be sufficient; you'd have a race condition between the time you were notified of the reparent, and the time you setexit_signal, in which the child could die.
Quoted 7 lines
Since
exit_signalis always reset to SIGCHLD before reparenting, this could be done by resetting it to a different signal; but at this point we're getting into a rather twisty method full of traps.-
exit_signalis reset to SIGCHLD onexec().Pretty much a plan-killer ;)
I can't understand why should we change ->exit_signal if we want to use signalfd. Yes, SIGCHLD is not rt. So what?
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do do_wait(WNOHANG) in a loop.
Confused.
Oleg.
Quoted 7 lines · Oleg Nesterov
I can't understand why should we change ->exit_signal if we want to use signalfd. Yes, SIGCHLD is not rt. So what?
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do
do_wait(WNOHANG)in a loop.
Well, a good reason why is that it makes things much easier to do in userspace. The NOTES sections of many of the syscall manpages are already too long with strange behaviours that you have to take into account every time (and which most people fail to do).
You may as well ask why we have signalfd() at all, and what was wrong with sigaction() and ordinary signal handlers? Well, lots of things actually; for a start, you couldn't really do much in the signal handler, so from userspace all we ended up doing was writing a byte to a pipe so we could wake up our main loop.
You then had to remember to exhaust this pipe *before* checking what signals were triggered, just in case a signal was delivered while you were checking (so at least you'd wake the main loop up once more to check).
signalfd() improves matters a lot; we don't have to worry about any strange behaviour, we just read() to know a signal is pending. But there were two competing implementations: one would just poll for read if signals were pending, but have no other detail; the other (which is what we have) gives us a siginfo_t for each pending signal.
Except or non-RT signals of course, in which it just gives us the siginfo_t for the first pending signal of that type; future ones are discarded.
I've suggested before that that's a bug in of itself, and that signalfd should always queue signals even if they're non-RT. But since, other than SIGCHLD, the only other signals with useful data are SIGILL, SIGFPE, SIGSEGV and SIGBUS (which have si_addr) which you kinda need to catch in a signal handler; and SIGPOLL (which has si_band and si_fd) which is negated by being in poll anyway, that's kinda hard to argue.
So let's compare userspace code for trying to reap children using signalfd(); to save space, I've omitted error handling and the select()/poll()/etc. call - assuming that the top half is initialisation, and the bottom half is inside the select() handler.
First, what we have today:
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
/* Block normal delivery and receive by sigfd instead */
sigprocmask (SIG_BLOCK, &mask, NULL);
sigfd = signalfd (-1, &mask, 0);
for (;;) {
read (sigfd, &fd_siginfo, sizeof siginfo);
/* throw away fd_siginfo, we're reading SIGCHLD and
* can't use it :-(
*/
/* SIGCHLD means _at_least_one_ child is pending, there
* may be more; so we have to loop AND expect to find
* nothing
*/
for (;;) {
/* ARGH! waitid returns 0 with WNOHANG if there
* are no children.
*
* AND the structure, despite being logically
* the same, isn't the same as the signalfd
* one :-/
*/
memset (&w_siginfo, 0, sizeof w_siginfo);
waitid (P_ALL, 0, &w_siginfo,
WEXITED | WNOHANG);
/* Did we find anything? */
if (! w_siginfo.si_pid)
break;
/* NOW we have the siginfo_t for a recently
* deceased process
*/
mourn (&w_siginfo);
}
/* Oh-HO!
* While we were looping in waitid(), other children may
* have died, and we probably already cleaned them up!
*
* But we'll still have a pending SIGCHLD, it might be
* tempting to clear that *BUT* the child could have
* died after the last waitid() call but before we clear
* it.
*
* We have no choice in this situation but to loop back
* through our entire main loop again, and do nothing.
*/
}Pros:
- code exists today
Cons:
- having siginfo_t returned by read() is pointless, we can't use it
- double loop isn't pretty
- strange waitid() API in case of WNOHANG and no child
- incompatible structures for signalfd()'s read result and waitid(), despite being logically the same structure! :-/
- can't simultaneously clear pending signal and wait, so we always have to go back round the main loop if a child dies after the read()
In fact, that's not what userspace code does at all.
Since there's no point listening to SIGCHLD, it's a complete no-op, we don't respond to it at all. We only need to use it to wake up the main loop. The wait() loop tends to be at the bottom of the main loop somewhere, completely outside of the fd processing.
Now, what if signalfd() would always queue pending signals even if they're non-RT? This would also be the same code if we could change SIGCHLD to SIGRTMIN for the *waiting* process's children:
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
/* Block normal delivery and receive by sigfd instead */
sigprocmask (SIG_BLOCK, &mask, NULL);
sigfd = signalfd (-1, &mask, 0);
for (;;) {
read (sigfd, &siginfo, sizeof siginfo);
/* siginfo is immediately useful!
*/
mourn (&siginfo);
/* we didn't clear off the wait queue, but that's easy
* since we have the pid from signalfd()
*/
waitid (P_PID, siginfo.si_pid, NULL, WEXITED);
}Pros:
- single siginfo_t structure type returned by read()
- we get information for each signal, we don't need a signal loop to find out what the signal is telling us
- exact match between the signal and the wait call
- no need to go round the main loop again just in case!
- child signal can now be processed just like anything else. This makes "standard main loop functions" (g_main_loop, etc.) much easier to write.
Cons:
- needs kernel patch
Personally, I think the fact this solves the case where you wait on a process that wasn't part of the original set the signal was pending for, so you have to process an extra SIGCHLD with nothing to do, is a good enough reason in of itself.
The overhead of going back through a main loop of a userspace process just to find out whether you already responded to a notification is a waste of cycles.
That's pretty neat, much nicer than what we had before. So what about waitfd() [I think I've slightly changed Casey's API here, or he changed my proposed one <g>]?
wfd = waitfd (-1, P_ALL, 0, WEXITED);
for (;;) {
read (wfd, &siginfo, sizeof siginfo);
/* siginfo is immediately useful AND the process has
* been reaped!
*/
mourn (&siginfo);
}Pros:
- we don't need to care about SIGCHLD anymore - why should we listen to a notification to call wait, if we can just call wait?
- the absolute easiest for a generic main loop; signals, timers and child death (as well as inotify, netlink, etc.) are now just cases of "select an fd, read structs of known size, process them"
- possibility to allow waitfd (WNOWAIT) on children outside of your own usual process tree - notification without reaping? Maybe too much?
Cons:
- needs kernel patch
- new API
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 6 lines · Scott James Remnant
Quoted 3 lines · Oleg Nesterov
I can't understand why should we change ->exit_signal if we want to use signalfd. Yes, SIGCHLD is not rt. So what?
Quoted 6 lines
Quoted 4 lines
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do
do_wait(WNOHANG)in a loop.Well, a good reason why is that it makes things much easier to do in userspace.
I never argued with this. And, let me repeat. I am not arguing against waitfd! Actually, I always try to avoid the "do we need this feature" discussions.
What I disagree with is that waitfd adds the functionality which does not exists currently.
Quoted 2 lines
You may as well ask why we have
signalfd()at all, and what was wrong withsigaction()and ordinary signal handlers? Well, lots of things
Cough. You don't have to explain me why signalfd is nice ;) I participated in discussion when it was created.
Quoted 9 lines
So let's compare userspace code for trying to reap children using
signalfd();First, what we have today:
[...snip the code...]
Pros:
- code exists today
That is what I meant. Not more.
Quoted 2 lines
Cons:
- havingsiginfo_treturned byread()is pointless, we can't use it
Indeed. We use read() only to wait for the signal death.
Quoted 1 line
- double loop isn't pretty
Nice argument to add the new syscall ;)
Quoted 1 line
- strange
waitid()API in case of WNOHANG and no child
Heh. I also don't like this ;) A reason for waitfd ?
Quoted 2 lines
- incompatible structures for signalfd()'s read result and
waitid(), despite being logically the same structure! :-/
I could blaim waitfd because it fills siginfo in the manner which is not compatible with signalfd, despite logically the same structure.
Quoted 2 lines
- can't simultaneously clear pending signal and wait, so we always have to go back round the main loop if a child dies after the
read()
Can't understand... waitfd doesn't clear the signal too?
And you forget to mention another drwaback with the current code:
a lot of pathetic comments ;)
Quoted 3 lines
Since there's no point listening to SIGCHLD, it's a complete no-op, we don't respond to it at all. We only need to use it to wake up the main loop.
Yes, sure, indeed, of course.
Quoted 2 lines
The
wait()loop tends to be at the bottom of the main loop somewhere, completely outside of the fd processing.
Huh.
Quoted 2 lines
Now, what if
signalfd()would always queue pending signals even if they're non-RT?
Well, I think this is off-topic, and more importantly I don't think this change is possible.
Quoted 2 lines
So what about
waitfd()
Yes, the user-space code (for this particular artificial example) becomes simpler. Following this logic, let's add sys_copyfile() to kernel? From time to time I regret we don't have it...
(from another thread)
Quoted 8 lines
Quoted 4 lines
I am not sure we are talking about the same thing, but afaics
poll()+ signalfd can work to (say) reap the childs. Actually,ppoll()alone is enough.Last time I checked,
ppoll()was not actually implemented across all architectures in a manner that solved the race it was intended to solve.I'd be delighted to learn that this had been fixed? :-)
Scott, this is unfair. Yes, some arches do not implement restore_sigmask() logic. So what? Let's suppose ppoll() has a bug. So, this means we should add waitfd? No, let's fix ppol(), and waitfd is orthogonal. Imho.
Again, again, again. Please don't forget about "I am not arguing against". But I don't buy your arguments.
Oleg.
Quoted 11 lines · Oleg Nesterov
Quoted 10 lines · Scott James Remnant
Quoted 7 lines · Oleg Nesterov
I can't understand why should we change ->exit_signal if we want to use signalfd. Yes, SIGCHLD is not rt. So what?
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do
do_wait(WNOHANG)in a loop.
Quoted 7 lines
Quoted 2 lines
Well, a good reason why is that it makes things much easier to do in userspace.
I never argued with this. And, let me repeat. I am not arguing against waitfd! Actually, I always try to avoid the "do we need this feature" discussions.
Unless I'm misinterpreting you, you're saying that you don't understand why we should change any current behaviour? My post is attempting to illustrate why we should.
Quoted 3 lines
What I disagree with is that waitfd adds the functionality which does not exists currently.
I'm not saying that it doesn't at all; in fact I gave an example of how you implement the exact same functionality today.
Changing the behaviour of signalfd() or introducing a call like waitfd() makes it easier to do things in userspace.
Quoted 5 lines
Quoted 2 lines
Cons:
- havingsiginfo_treturned byread()is pointless, we can't use itIndeed. We use
read()only to wait for the signal death.
In fact, because main loops use select()/poll(), for the SIGCHLD case you'd never use signalfd() at all!
Unless I'm missing something, the following two examples are identical in behaviour:
using signalfd:
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
sigprocmask (SIG_BLOCK, &mask, NULL);
sigfd = signalfd (-1, &mask, 0);
/* prepare signal set */
FD_SET (sigfd, &readfds);
nfds = sigfd > nfds ? sigfd + 1 : nfds;
for (;;) {
select (nfds, &readfds, NULL, NULL, NULL);
if (FD_ISSET (sigfd, &readfds))
for (;;)
read (sigfd, &fdsi, sizeof fdsi);
/* SIGCHLD only notifies us that wait() can be called
* at least once without blocking.
*
* Since we have to call it multiple times anyway,
* and since it may block, all SIGCHLD really does it
* wakes up our main loop.
*
* Thus unconditionally loop on wait every time through
*/
while ((pid = waitpid (-1, &status, WNOHANG)) == 0)
mourn (pid, status);
}using pselect:
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
sigprocmask (SIG_BLOCK, &mask, &origmask);
/* prepare signal set */
for (;;) {
pselect (nfds, &readnfds, NULL, NULL, NULL,
&origmask);
/* SIGCHLD only needs to wake up our main loop,
* unconditionally loop on wait every time through
*/
while ((pid = waitpid (-1, &status, WNOHANG)) == 0)
mourn (pid, status);
}But the pselect() version is neater. Which is why I started the previous reply off with "why have signalfd() at all?"
Quoted 8 lines
Quoted 2 lines
- can't simultaneously clear pending signal and wait, so we always have to go back round the main loop if a child dies after the
read()Can't understand... waitfd doesn't clear the signal too?
And you forget to mention another drwaback with the current code:
a lot of pathetic comments ;)
The comments were illustrating the sources of userspace frustration with the current syscalls.
One of them was attempting to explain what you don't understand here, I'll try and be more verbose...
We only want to be woken up in our main loop if we have something to do. One of those things that we need to do is reap dead child processes.
The kernel provides a couple of methods of being woken up because of that event; we can use signalfd() for SIGCHLD, or we could use pselect() and otherwise mask SIGCHLD so that any pending signal wakes up the main loop.
That notification only tells us that *at least one* process has died; SIGCHLD may only be pending once at a time. If further children die before we clear the signal, nothing will happen.
We clear the signal with read() on the signalfd, or it's inherently cleared inside pselect() by allowing it to be delivered to ourselves.
It will not be cleared again until we read() again, or call pselect() again.
Because it only tells us that *at least one* process has died, we have to call waitpid() repeatedly until we have exhausted the wait queue.
~~Calling waitpid() does not clear the pending signal.~~
This is the important bit.
If a further process dies while we're inside the waitpid() loop, we will most likely reap that straight away. But this does not clear the pending signal. The main loop will be woken up again, even though it does not need to be.
Thus:
- child process #1 dies
- main loop woken up by SIGCHLD
- pending status of signal cleared
- enter wait loop
- child process #2 dies
- SIGCHLD pending again
- waitpid() called first time, child process #1 reaped
- waitpid() called second time, child process #2 reaped (SIGCHLD still pending)
- waitpid() called third time, no child processes remain
- exit wait loop
- back to top of main loop, immediately woken up by pending SIGCHLD
- pending status of signal cleared
- enter wait loop
- waitpid() called first time, but no child processes remain (we reaped it last time round)
- exit wait loop
- back to top of main loop, sleep
A rookie mistake is to assume that you receive SIGCHLD every time a child process dies. Nearly everyone writes this first time out:
signal (SIGCHLD, reaper);
void
reaper (int signum)
{
pid = waitpid (-1, &status, 0);
printf ("%d died! :-(\n", pid); // yes, I know
}You may even be encouraged to believe that will work, because you've read that SIGCHLD is masked while the signal handler is running. Of course, we all know that many children may die between the time the first child dies (that makes the signal pending) and the signal handler actually executing.
W. Richard Stevens even makes this mistake in Advanced Programming in the UNIX Environment (p. 605), he even justifies called waitpid() in the signal handler because multiple children can die.
He doesn't help matters in the second edition of UNIX Network Programming v1 where he defines exactly the same function (p. 122) though he at least goes on to explain all the errors and define a correct function that loops on waitpid() (p. 128)
Seasoned developers might smile wryly, but I've seen them make another error. Presumably because they know about select()/poll(), they appear to treat SIGCHLD accordingly.
"SIGCHLD is to waitpid() as select() is to read()"
or perhaps more verbosely:
"A pending SIGCHLD means that a call to waitpid() won't block"
We know that this is completely wrong.
SIGCHLD and the wait queue are entirely separate, the only connection is that SIGCHLD will be made pending, if not already, when a new entry is added to the wait queue.
However since the two are cleared by different means, the following behaviours can all be exhibited:
- SIGCHLD not pending, but waitpid() will not block
This is true in all example usage; after you've called the read() on the signalfd - or the pselect() has woken, SIGCHLD is probably no longer pending but waitpid() will not block
Compare with select() behaviour; if you fail to read() from the fd, select() wakes up yet again
- SIGCHLD pending, but waitpid() will block
This is true if you exhaust the wait queue in a loop,
All SIGCHLD is useful for is to get your main loop out of select()/poll(); you must always exhaust the wait queue every time you have woken up.
(I think we agreed on this, I'm trying to explain the next paragraph.)
Unfortunately because the wake up, and the wait queue, are *not* connected; we can be woken up when we do not need to be.
Quoted 5 lines
Quoted 2 lines
The
wait()loop tends to be at the bottom of the main loop somewhere, completely outside of the fd processing.Huh.
From the above reasoning, SIGCHLD is only there to wake up your main loop. If you fail to exhaust the wait queue, you will not be woken up again until another child process dies, so you must exhaust the wait queue.
Now, we can try and work out whether it was SIGCHLD that woke up our main loop. If using signalfd(), we would read() and check ssi_pid; if using pselect() we'd have to have a signal handler that sets a sigatomic_t.
We could check that, and then only loop on waitpid() if set.
But why?
We've proved above that SIGCHLD being pending does not mean that waitpid() will not block. We're not actually saving ourselves from the extra iteration of the main loop, or even saving ourselves from the first call to waitpid() that returns -1.
Arguably the extra effort to check whether SIGCHLD was pending is more expensive (certainly in code readability) than just calling waitpid() anyway.
Thus it's most common (certainly in the standard libraries I've read) to just always loop on waitpid().
Quoted 6 lines
Quoted 2 lines
Now, what if
signalfd()would always queue pending signals even if they're non-RT?Well, I think this is off-topic, and more importantly I don't think this change is possible.
From the argument above, it should be reasonably clear that what this patch is attempting to do is join up the "waking up the main loop" from the "clearing of the wait queue".
In simpler words, make wait behave more like select().
waitfd() does that absolutely directly. It allows you to select() on the wait queue itself. Clearing the wait queue, and the main loop wake-up, are directly connected.
We eliminate the problem of the extra main loop wake-up and iteration, because if we exhaust the wait queue then the waitfd socket will not poll for reading.
We also eliminate the problem where if we fail to exhaust the wait queue, we will not be woken up, because the waitfd socket will still poll for reading.
If you're using waitfd, SIGCHLD is unnecessary.
I'm not personally arguing for waitfd() *itself*, just that there be some way of coupling the main loop wake up with the actual contents of the wait queue.
If signalfd() can be used for this, I'd be happy as a clam.
But today, signalfd() doesn't help at all because it still has all the same drawbacks.
We have an extra main loop wake-up and iteration if a process dies while we're exhausting the wait queue because SIGCHLD is pending again.
If we fail to exhaust the wait queue, the main loop will not be woken up because SIGCHLD is not pending - despite the wait queue not being empty.
It's actually completely possible, in fact, it's almost a one-line patch.
signal.c renamed from signal.c~ +3 -1
@@ -816,8 +816,10 @@* exactly one non-rt signal, so that we can get more * detailed information about the cause of the signal. */ -if (legacy_queue (pending, sig)) +if (legacy_queue (pending, sig)) { +signalfd_notify (t, sig);return 0 ; + }/* * fast-pathed signals for kernel-internal things like SIGSTOP * or SIGKILL.
There might be considerations here I haven't thought of, but it worked pretty well for me.
This isn't waitfd(), you're not actually selecting on the wait queue. But it means you'll have a queue of SIGCHLDs for each entry in the wait queue.
You still have to remember that for each SIGCHLD you read from the signalfd, you must call waitpid on that pid, but if you don't do that - it'll still poll and you can go round again.
Personally I think waitfd() is more elegant, and has a rather nice symmetry with the other system calls.
Quoted 7 lines
Quoted 2 lines
So what about
waitfd()Yes, the user-space code (for this particular artificial example) becomes simpler. Following this logic, let's add
sys_copyfile()to kernel? From time to time I regret we don't have it...
Well, I don't think that argument is quite orthogonal because copyfile() can be implemented in userspace with identical behaviour to a kernel syscall (open, fstat, sendfile, chown/etc.).
A more orthogonal example would be pselect(). That implemented, in the kernel, a syscall that it actually wasn't possible to implement in userspace in _quite_ the same way. The procmask/select sequence was known to be racy, so we all worked around it with an interrupt pipe.
The argument for waitfd() or similar in the kernel is because there are races in userspace that we can't solve.
Hopefully you can see my argument now? :-)
Quoted 2 lines
(from another thread)
As noted there, I replied separately and cut the discussion about this because I didn't want to confuse the matter.
Scott
Signature · 2 lines
Scott James Remnant [email protected]
Quoted 10 lines · Scott James Remnant
Quoted 4 lines · Oleg Nesterov
I never argued with this. And, let me repeat. I am not arguing against waitfd! Actually, I always try to avoid the "do we need this feature" discussions.
Unless I'm misinterpreting you, you're saying that you don't understand why we should change any current behaviour? My post is attempting to illustrate why we should.
Scott. How many times should I repeat: I am _not_ arguing against waitfd.
But to clarify, neither I vote for it. I don't really care. Except I do care about the code if it will be merged, that is why I entered this thread.
Quoted 5 lines
Quoted 3 lines
What I disagree with is that waitfd adds the functionality which does not exists currently.
I'm not saying that it doesn't at all; in fact I gave an example of how you implement the exact same functionality today.
This means I was confused. Because I thought you point is we can't poll for childs without signalfd. And all I asked was: why do you think so. I do understand that waitfd can be handy.
Quoted 9 lines
In fact, because main loops use select()/poll(), for the SIGCHLD case you'd never use
signalfd()at all!Unless I'm missing something, the following two examples are identical in behaviour:
using signalfd:
...
using pselect:
Yes, and that is why I mentioned that ppoll() alone is enough.
Quoted 2 lines
But the
pselect()version is neater. Which is why I started the previous reply off with "why havesignalfd()at all?"
Unlike waitfd, there are things which we just can not do without signalfd, even if we have ppol/pselect. For example: wait for the signal, but not dequeue it.
Quoted 24 lines
One of them was attempting to explain what you don't understand here, I'll try and be more verbose...
...
~~Callingwaitpid()does not clear the pending signal.~~This is the important bit.
If a further process dies while we're inside the
waitpid()loop, we will most likely reap that straight away. But this does not clear the pending signal. The main loop will be woken up again, even though it does not need to be.Thus:
- child process #1 dies
- main loop woken up by SIGCHLD
- pending status of signal cleared
- enter wait loop
- child process #2 dies
- SIGCHLD pending again
-waitpid()called first time, child process #1 reaped
-waitpid()called second time, child process #2 reaped (SIGCHLD still pending)
-waitpid()called third time, no child processes remain
Quoted 8 lines
- exit wait loop
- back to top of main loop, immediately woken up by pending SIGCHLD
- pending status of signal cleared
- enter wait loop
-waitpid()called first time, but no child processes remain (we reaped it last time round)
- exit wait loop
- back to top of main loop, sleep
Scott, I don't really understand why are you trying to explain this all to me. I do understand this. At least I hope ;)
Yes this is possible, and I see no problems here.
Quoted 6 lines
- SIGCHLD not pending, but
waitpid()will not blockThis is true in all example usage; after you've called the
read()on the signalfd - or thepselect()has woken, SIGCHLD is probably no longer pending butwaitpid()will not block
Quoted 6 lines
Compare with
select()behaviour; if you fail toread()from the fd,select()wakes up yet again- SIGCHLD pending, but
waitpid()will blockThis is true if you exhaust the wait queue in a loop,
... and this too.
Quoted 3 lines
All SIGCHLD is useful for is to get your main loop out of select()/poll(); you must always exhaust the wait queue every time you have woken up.
Yes, and yes, and yes. Scott, I am sorry, I failed to read to the end so perhaps I missed something ;)
signal.c line 822
Quoted 11 lines
signal.c renamed from signal.c~ +3
-1@@ -816,8 +816,10 @@* exactly one non-rt signal, so that we can get more * detailed information about the cause of the signal. */ -if (legacy_queue (pending, sig)) +if (legacy_queue (pending, sig)) { +signalfd_notify (t, sig);return 0 ; + }
I'd prefer to not discuss this here, but I am not sure I understand. There should not be no threads which need the wakeup from here, and I can't see how this change can help.
Quoted 3 lines
A more orthogonal example would be
pselect(). That implemented, in the kernel, a syscall that it actually wasn't possible to implement in userspace
Yes, exactly,
Quoted 2 lines
The argument for
waitfd()or similar in the kernel is because there are races in userspace that we can't solve.
And now I don't understand you again. Please show me which races we _can not_ solve in userspace without waitfd?
Yes we can race with the exiting childs while doing waitpid() in a loop, so we can make the unnecessary syscall. But please do not tell me _this_ is the race we can't solve. This is _harmless_. Unlike the problems with the poor user-space implementations of pselect/ppol.
Oleg.
Quoted 21 lines · Oleg Nesterov
Quoted 12 lines
Thus:
- child process #1 dies
- main loop woken up by SIGCHLD
- pending status of signal cleared
- enter wait loop
- child process #2 dies
- SIGCHLD pending again
-waitpid()called first time, child process #1 reaped
-waitpid()called second time, child process #2 reaped (SIGCHLD still pending)
-waitpid()called third time, no child processes remainQuoted 8 lines
- exit wait loop
- back to top of main loop, immediately woken up by pending SIGCHLD
- pending status of signal cleared
- enter wait loop
-waitpid()called first time, but no child processes remain (we reaped it last time round)
- exit wait loop
- back to top of main loop, sleep
Quoted 2 lines
Scott, I don't really understand why are you trying to explain this all to me. I do understand this. At least I hope ;)
Indeed :)
Scott, you're teaching Linux signals to the guy that is likely the de-facto mantainer of the subsystem. See the irony?
- Davide
Quoted 4 lines · Scott James Remnant
That's pretty neat, much nicer than what we had before. So what about
waitfd()[I think I've slightly changed Casey's API here, or he changed my proposed one <g>]?
I think you might have changed the argument order slighly, but other than that, counting the corrections I've made from feedback here, its about the same.
--CJD
Quoted 35 lines · Scott James Remnant
First, what we have today:
sigemptyset (&mask); sigaddset (&mask, SIGCHLD); /* Block normal delivery and receive by sigfd instead */ sigprocmask (SIG_BLOCK, &mask, NULL); sigfd = signalfd (-1, &mask, 0); for (;;) { read (sigfd, &fd_siginfo, sizeof siginfo); /* throw away fd_siginfo, we're reading SIGCHLD and * can't use it :-( */ /* SIGCHLD means _at_least_one_ child is pending, there * may be more; so we have to loop AND expect to find * nothing */ for (;;) { /* ARGH! waitid returns 0 with WNOHANG if there * are no children. * * AND the structure, despite being logically * the same, isn't the same as the signalfd * one :-/ */ memset (&w_siginfo, 0, sizeof w_siginfo); waitid (P_ALL, 0, &w_siginfo, WEXITED | WNOHANG); /* Did we find anything? */ if (! w_siginfo.si_pid)
Quoted 9 lines
break; /* NOW we have the siginfo_t for a recently * deceased process */ mourn (&w_siginfo); }
That, once all the glamorous comments are removed, can be solved pretty much with this much userspace code:
int waitfd(int flags) {
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);
return signalfd(-1, &mask, flags);
}int waitfd_read(int fd, siginfo_t *si) {
for (;;) {
if (read(sigfd, &fd_siginfo,
sizeof fd_siginfo) != sizeof(fd_siginfo)
retrun 0;
memset(si, 0, sizeof *si);
waitid(P_ALL, 0, si, WEXITED | WNOHANG);
if (si->si_pid)
break;
}
return sizeof *si;
}About the exit_signal, that would have made probably more sense for it to be a parent property, instead of childs one. And be:
do_notify_parent(p, p->parent->exit_signal);
instead of:
do_notify_parent(p, p->exit_signal);
After all, is the parent that is going to make use of the notification, not the child.
- Davide
Quoted 29 lines · Oleg Nesterov
Quoted 20 lines · Scott James Remnant
Quoted 6 lines · Roland McGrath
Do we really need another one for this? How about using signalfd plus setting the child's
exit_signalto a queuing (SIGRTMIN+n) signal instead of SIGCHLD? It's slightly more magical for the userland process to know to do that (fork -> clone SIGRTMIN). But compared to adding a syscall we don't really have to add, maybe better.This wouldn't help the init daemon case:
- the
exit_signalis set on the child, not on the parent.While the init daemon could
clone()every new process and setexit_signal, this would not be set for processes reparented to init.Even if we had a new syscall to change the
exit_signalof a given process, *and* had the init reparent notification patch, this still wouldn't be sufficient; you'd have a race condition between the time you were notified of the reparent, and the time you setexit_signal, in which the child could die.Quoted 7 lines
Since
exit_signalis always reset to SIGCHLD before reparenting, this could be done by resetting it to a different signal; but at this point we're getting into a rather twisty method full of traps.-
exit_signalis reset to SIGCHLD onexec().Pretty much a plan-killer ;)
Quoted 8 lines
I can't understand why should we change ->exit_signal if we want to use signalfd. Yes, SIGCHLD is not rt. So what?
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do
do_wait(WNOHANG)in a loop.Confused.
I know I am terribly late for the party :)
"do_wait(WNOHANG) in a loop" is a performance problem.
Oleg, do you remember that strace bug when it was swamped with gazillions of stop notifications from a multithreaded task, then by dealing with them one-by-one it was causing unfairness and ultimately "this program never finishes when run under strace" bug?
And another typical nuisance that running multithreaded stuff under strace is much slower, even with -e option which limits the set of decoded syscalls?
Having waitfd would help both cases: strace can gulp a lot of waitpid notifications in one go, and batch process them.
Signature · 1 line
vda
Quoted 7 lines · Denys Vlasenko
Quoted 5 lines · Oleg Nesterov
We do not need multiple signals in queue if we want to reap multiple zombies. Once we have a single SIGCHLD (reported by signalfd or whatever) we can do
do_wait(WNOHANG)in a loop.
Quoted 5 lines
Quoted 1 line
Confused.
I know I am terribly late for the party :)
"
do_wait(WNOHANG)in a loop" is a performance problem.
Yes.
Quoted 5 lines
Oleg, do you remember that strace bug when it was swamped with gazillions of stop notifications from a multithreaded task, then by dealing with them one-by-one it was causing unfairness and ultimately "this program never finishes when run under strace" bug?
Yes. But, iirc, this was not connected to the performance problems with do_wait(). The problem was, strace did a single do_wait() instead of wait-them-all.
Quoted 3 lines
And another typical nuisance that running multithreaded stuff under strace is much slower, even with -e option which limits the set of decoded syscalls?
IIUC, this is also because strace is single-threaded, I mean it doesn't scale well.
Quoted 3 lines
Having waitfd would help both cases: strace can gulp a lot of waitpid notifications in one go, and batch process them.
Perhaps.
I do not know how much do_wait() contributes to the slowness though. And it is not exactly clear how we can implement the "fast" waitfd.
For example, this patch (iirc!) just calls do_wait() in a loop. I doubt very much it can really help to improve the performance.
Oh. Can't resist. The real problem is that ptrace API should not be per-thread, and it should not use wait() at all. But this is offtopic.
Oleg.