From 48ea61ab9c369ab643b0909a5f5873b2b3c26bb7 Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Mon, 8 May 2017 00:36:04 +0200 Subject: [PATCH 1/2] newlib: more tests for signals --- qa/lib/newlib/202-signals.c | 55 ++++++++++++++++++++++++++++ qa/lib/newlib/203-signals.c | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 qa/lib/newlib/202-signals.c create mode 100644 qa/lib/newlib/203-signals.c diff --git a/qa/lib/newlib/202-signals.c b/qa/lib/newlib/202-signals.c new file mode 100644 index 0000000..eee052a --- /dev/null +++ b/qa/lib/newlib/202-signals.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +void sigcont() { + printf("CHILD: Got SIGCONT\n"); + exit(0); +} + +int +main() { + int pid, p[2], child, cstatus; + char dummy[1]; + + /* get child process */ + + if(pipe(p)){ + perror("pipe"); + exit(1); + } + if ((pid = fork()) < 0) { + perror("fork"); + exit(2); + } + + if (pid == 0) { + signal(SIGCONT,sigcont); /* set function calls */ + printf("Child going to loop...\n"); + close(p[1]); + close(p[0]); + for(;;); /* loop for ever */ + } + else /* parent */ + { + close(p[1]); + if(read(p[0], &dummy, 1) > 0){ + printf("sync read received data"); + exit(EXIT_FAILURE); + } + close(p[0]); + printf("PARENT: sending SIGCONT\n\n"); + kill(pid,SIGCONT); + child = wait(&cstatus); + if(child == pid && cstatus == 0) + exit(0); + else + { + printf("PARENT: child exited with status %d\n", cstatus); + exit(1); + } + } +} + diff --git a/qa/lib/newlib/203-signals.c b/qa/lib/newlib/203-signals.c new file mode 100644 index 0000000..9d81e9e --- /dev/null +++ b/qa/lib/newlib/203-signals.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +void sigcont() { + signal(SIGCONT,sigcont); /* reset signal */ + printf("CHILD: Got SIGCONT\n"); + exit(0); +} + +void sigstop() { + signal(SIGSTOP,sigstop); /* reset signal */ + printf("CHILD: Got SIGSTOP\n"); + exit(1); +} + +int +main() { + int pid, p[2], child, cstatus; + char dummy[1]; + + /* get child process */ + + if(pipe(p)){ + perror("pipe"); + exit(1); + } + if ((pid = fork()) < 0) { + perror("fork"); + exit(2); + } + + if (pid == 0) { + signal(SIGCONT,sigcont); /* set function calls */ + signal(SIGSTOP,sigstop); /* set function calls */ + printf("Child going to loop...\n"); + close(p[1]); + close(p[0]); + for(;;){ + /* loop for ever */ + printf("."); + usleep(1000); + } + } + else /* parent */ + { + close(p[1]); + if(read(p[0], &dummy, 1) > 0){ + printf("sync read received data"); + exit(EXIT_FAILURE); + } + close(p[0]); + sleep(2); + printf("PARENT: sending SIGSTOP\n"); + kill(pid,SIGSTOP); + sleep(4); + + printf("PARENT: sending SIGCONT\n"); + kill(pid,SIGCONT); + + child = wait(&cstatus); + if(child == pid && cstatus == 0) + exit(0); + else + { + printf("PARENT: child exited with status %d\n", cstatus); + exit(1); + } + } +} + From edc116ef454029a387443835ca2b948fbf32b285 Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Mon, 8 May 2017 00:43:35 +0200 Subject: [PATCH 2/2] libposix: documented new signal machinery --- sys/src/lib/posix/signals.c | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/sys/src/lib/posix/signals.c b/sys/src/lib/posix/signals.c index f32eeeb..1bb0acf 100644 --- a/sys/src/lib/posix/signals.c +++ b/sys/src/lib/posix/signals.c @@ -15,6 +15,88 @@ * You should have received a copy of the GNU Affero General Public License * along with Jehanne. If not, see . */ + +/* POSIX signals have weird semantics that are hard to emulate in a + * sane system without giving up on its sanity. + * + * We distinguish control signals (SIGKILL, SIGSTOP, SIGCONT, + * SIGABRT, SIGIOT), SIGCHLD/SIGCLD, timers' wakeups + * (SIGALRM, SIGPROF, SIGVTALRM) and all the others. + * + * # TRASMISSION + * + * Signal transmission depends on the relation between the sender + * and the receiver: + * 1) if sender and receiver have no relation the signal is translated + * to a note and sent to the receiver(s); + * 2) if sender == receiver the signal trampoline is directly invoked + * for all signals except control ones and the default disposition occurs if + * the trampoline does not handle the signal + * 3) if sender is parent or child of receiver the transmision + * differs from the default if libposix_emulate_SIGCHLD() has been + * called during library initialization + * + * Since notes in Jehanne are not reentrant, signals translated to + * notes will be enqueued in kernel. A special machinery is implemented + * for timers, so that they can be used in signal handlers. + * + * For all the signals except SIGCONT, the burden of interpreting the + * signal is on the receiver: the sender just send the signal. + * + * The receiver will translate the note back to the appropriate signal + * number and invoke the trampoline: if trampoline returns 0 no function + * registered with signal() handled the signal and the library will + * invoke the default disposition associated to the signal. + * + * # CONTROL MESSAGES + * + * Control messages are translated by the receiver to equivalent actions: + * - SIGKILL => write "kill" to the control file of the current process + * - SIGSTOP => write "stop" to the control file of the current process + * - SIGABRT/SIGIOT => invoke the registered signal handlers and abort + * the current process + * - SIGCONT => invoke the registered signal handlers via the signal + * trampoline and continue; note that (since a stopped + * process cannot resume itself) the sender will write + * "start" to the control file of the receiver before + * sending the note (unless SIGCHLD emulation is enable). + * + * # SIGCHLD/SIGCLD + * + * Jehanne (like Plan 9) does not support a SIGCHLD equivalent. + * The user space emulation provided here is quite expensive, so it's + * disabled by default. + * Calling libposix_emulate_SIGCHLD() during libposix initialization + * will enable this machinery for the whole life of the process. + * + * Such emulation change the way fork and kill works. + * + * Each fork() will spawn two additional processes that are designed + * to proxy signals between the parent and the desired child: + * + * parent + * +- proxy from parent to child (P2C) + * +- proxy from child to parent (C2P) + * +- child + * + * Fork will return the pid of P2C to the parent, so that the + * parent process will see the first proxy as its child; the child will + * see the second as its parent. Each proxy waits for its child and + * forwards the notes it receive to its designed target. + * Finally fork in child will return 0. + * + * When the child exits, C2P will send a SIGCHLD note to parent and + * then exit with the same status. Then P2C will exit with + * the same status too. + * + * As the parent process will see P2C as its child, it will send any + * signal to it via kill and will wait it on SIGCHLD. + * However, when this machinary is enabled, kill will treat all signals + * for forked children in the same way, sending them to P2C. + * It's P2C's responsibility to translate control messages as required, + * so that SIGCONT will work as expected. + */ + #include #include #include