On Tue, 2005-08-09 at 14:27 -0700, Linus Torvalds wrote: > On the other hand, the standard seems to be a bit confused according to > google: > > "This mask is formed by taking the union of the current signal mask and > the value of the sa_mask for the signal being delivered unless > SA_NODEFER or SA_RESETHAND is set, and then including the signal being > delivered. If and when the user's signal handler returns normally, the > original signal mask is restored." > > Quite frankly, the way I read it is actually the old Linux behaviour: the > "unless SA_NODEFER or SA_RESETHAND is set" seems to be talking about the > whole union of the sa_mask thing, _not_ just the "and the signal being > delivered" part. Exactly the way the kernel currently does (except we > should apparently _also_ do it for SA_RESETHAND).
Actually I take it the other way. The wording is awful. But the "unless SA_NODEFER or SA_RESETHAND is set, and then including the signal being delivered". This looks to me that it adds the signal being delivered to the blocked mask unless the SA_NODEFER or SA_RESETHAND is set. I kind of wonder if English is the native language of those that wrote this. > > So if we decide to change the kernel behaviour, I'd like this to be in -mm > for a while before merging (or merge _very_ early after 2.6.13). I could > imagine this confusing some existing binaries that had only been tested > with the old Linux behaviour, regardless of what a standard says. > Especially since the standard itself is so confusing and badly worded. > > Maybe somebody can tell what other systems do, since I assume the standard > is trying to describe behaviour that actually exists in the wild.. Well, I wrote this (attached) test program to see how signals are affected by different settings. It's best run under an idle system, so if someone has another unix out there that can run this and return the results, we can see how they work. This test program has possible race conditions but should work fine on an idle system. But that's just the nature of testing signals and other asynchronous activities, as well as just writing something up in a couple of minutes ;-) A non-modified Linux returns this: sa_mask blocks other signals SA_NODEFER does not block other signals SA_NODEFER affects sa_mask SA_NODEFER and sa_mask does not block sig !SA_NODEFER blocks sig SA_NODEFER does not block sig sa_mask blocks sig This shows the following: 1. That signals in sa_mask are blocked while the signal handler is running. 2. SA_NODEFER does not by itself block other signals (this should never be anything else). 3. SA_NODEFER does affect the sa_mask (which is the topic of this discussion). 4. When SA_NODEFER is set and sa_mask has the signal itself set, then the signal is blocked (I believe that this is wrong too. If the signal is itself in sa_mask then SA_NODEFER should still not let it run. This should be interresting to see what other unices do). 5. When SA_NODEFER is not set, the signal is blocked (this is correct). 6. When SA_NODEFER is set (with nothing in sa_mask) the signal is not blocked (also correct). 7. When the signal itself is set in sa_mask, then the signal is blocked (correct). With a patched kernel we get the following output: sa_mask blocks other signals SA_NODEFER does not block other signals SA_NODEFER does not affect sa_mask SA_NODEFER and sa_mask blocks sig !SA_NODEFER blocks sig SA_NODEFER does not block sig sa_mask blocks sig Here the differences are that SA_NODEFER does _not_ affect the sa_mask, and that when both the sig is in sa_mask and the SA_NODEFER is set, then the signal is still blocked. I can see this being useful if the sa_mask is generated, and the NODEFER is expected to be the default. This allows for overriding the default. So, if someone can run this test on another unix system, then we can have an idea of how others handle this. -- Steve
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> static int u1; static int u2; static void user1(int x) { /* for testing against itself */ if (u1) u2 = 1; u1 = 1; sleep(5); u1 = 0; } static void user2(int x) { if (u1) u2 = 1; } static void intr(int x) { exit(u2); } static void start(struct sigaction *act) { struct sigaction a; memset(&a,0,sizeof(a)); a.sa_handler = intr; if ((sigaction(SIGINT,&a,NULL)) < 0) { perror("sigaction"); exit(-1); } /* * This is the testing handler */ act->sa_handler = user1; if ((sigaction(SIGUSR1,act,NULL)) < 0) { perror("sigaction"); exit(-1); } a.sa_handler = user2; if ((sigaction(SIGUSR2,&a,NULL)) < 0) { perror("sigaction"); exit(-1); } for (;;) ; } int testsig(struct sigaction *act, int sig1, int sig2) { int pid; int status; if ((pid = fork()) < 0) { perror("fork"); } else if (!pid) { /* * Test1 sa_mask includes SIGUSR2 */ start(act); exit(0); } /* * Send first signal to start the test. */ kill(pid,sig1); /* * SIGUSR1 sleeps for 5, just sleep for on here * to make sure the system got it. */ sleep(1); /* * Send the second signal to the child, to see if * this wakes it up. */ kill(pid,sig2); sleep(1); /* * End the test. */ kill(pid,SIGINT); waitpid(pid,&status,0); return WEXITSTATUS(status); } int main(int argc, char **argv) { struct sigaction act; int ret; memset(&act,0,sizeof(act)); sigaddset(&act.sa_mask,SIGUSR2); ret = testsig(&act,SIGUSR1,SIGUSR2); if (ret == 1) { printf("sa_mask does not block other signals\n"); } else if (ret == 0) { printf("sa_mask blocks other signals\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); act.sa_flags |= SA_NODEFER; ret = testsig(&act,SIGUSR1,SIGUSR2); if (ret == 1) { printf("SA_NODEFER does not block other signals\n"); } else if (ret == 0) { printf("SA_NODEFER blocks other signals\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); act.sa_flags |= SA_NODEFER; sigaddset(&act.sa_mask,SIGUSR2); ret = testsig(&act,SIGUSR1,SIGUSR2); if (ret == 1) { printf("SA_NODEFER affects sa_mask\n"); } else if (ret == 0) { printf("SA_NODEFER does not affect sa_mask\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); act.sa_flags |= SA_NODEFER; sigaddset(&act.sa_mask,SIGUSR1); ret = testsig(&act,SIGUSR1,SIGUSR1); if (ret == 1) { printf("SA_NODEFER and sa_mask does not block sig\n"); } else if (ret == 0) { printf("SA_NODEFER and sa_mask blocks sig\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); ret = testsig(&act,SIGUSR1,SIGUSR1); if (ret == 1) { printf("!SA_NODEFER does not block sig\n"); } else if (ret == 0) { printf("!SA_NODEFER blocks sig\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); memset(&act,0,sizeof(act)); act.sa_flags |= SA_NODEFER; ret = testsig(&act,SIGUSR1,SIGUSR1); if (ret == 1) { printf("SA_NODEFER does not block sig\n"); } else if (ret == 0) { printf("SA_NODEFER blocks sig\n"); } else { printf("Unknown return code!!\n"); } memset(&act,0,sizeof(act)); sigaddset(&act.sa_mask,SIGUSR1); ret = testsig(&act,SIGUSR1,SIGUSR1); if (ret == 1) { printf("sa_mask does not block sig\n"); } else if (ret == 0) { printf("sa_mask blocks sig\n"); } else { printf("Unknown return code!!\n"); } exit(0); exit(0); }