3

I have a program from a (oldish) text book that is intended to illustrate the use of POSIX signals on UNIX. The program runs a computational loop to compute perfect numbers starting at a fixed point.

  • A time alarm signal is used to periodically print status.
  • An interrupt signal is used for status on demand.
  • A quit signal is used to reset the test interval (or terminate).

void perfect(int);

sigjmp_buf jmpenv; /* environment saved by setjmp*/

int n; /* global variable indicating current test point */

int main() {

    int begin; /* starting point for next search*/
        /* interrupt routines*/
    void status();
    void query();

    sigset_t mask;
    struct sigaction action;


    if (sigsetjmp(jmpenv,0)) {
        printf("Enter search starting point (0 to terminate): ");
        scanf("%d",&begin);
        if (begin==0) exit(0);
        sigprocmask(SIG_UNBLOCK, &mask, NULL);
        }
    else begin=2;

    /* Status Routine will handle timer and INTR */

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGALRM);
    sigaddset(&mask, SIGQUIT);
    action.sa_flags=0;
    action.sa_mask=mask;

    action.sa_handler=status;
    sigaction(SIGINT,&action,NULL);
    sigaction(SIGALRM,&action,NULL);

    action.sa_handler=query;
    sigaction(SIGQUIT,&action,NULL);

    /* start alarm clock */
    alarm(20);
    perfect(begin);
}

void perfect(start) 
    int start;
{
    int i,sum;

    n=start;

while (1) {
    sum=1;
    for (i=2;i<n;i++)
        if (!(n%i)) sum+=i;

    if (sum==n) printf("%d is perfect\n",n);
    n++;
    }
}

void status(signum) 
int signum;
{

    alarm(0); /* shutoff alarm */

    if (signum == SIGINT) printf("Interrupt ");
    if (signum == SIGALRM) printf("Timer ");

    printf("processing %d\n",n);

    alarm(20);  /*restart alarm*/
}   

void query() {siglongjmp(jmpenv,1);}

My questions are:

  1. Why does it call void status(); and void query(); in the main at first?
  2. After the if statement checks begin == 0 and decides to exit, it follows a line called "sigprocmask(SIG_UNBLOCK, &mask, NULL);" Why do I have to make it unblock after I exit already?
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
jackhao
  • 3,457
  • 3
  • 22
  • 43
  • 3
    What on earth are you doing writing K&R-style (non-prototype) function definitions? Never write new code other than using prototype notation! (At the very least, you need to explain why that might necessary; no platform that supports POSIX has a C compiler that does not support prototypes, so you've got to talk very persuasively.) – Jonathan Leffler Dec 18 '13 at 01:10
  • @JonathanLeffler I didn't write the function. This is the code from book and i'm trying to understand it. – jackhao Dec 18 '13 at 01:11

1 Answers1

5
  1. Those two lines declare the functions status() and query() as returning void. They don't specify what arguments they take. In modern C, declaring functions inside another function is anathema (and declaring them without full prototypes too is anathema — but that seems to be a discussion for another day since it isn't your code). Functions should be declared outside other functions, and if they're defined or used in another file, they should be declared in a header. If they're defined in the current file and not used in any other file, they should be declared and defined as static functions.

  2. The variable begin is initialized unorthodoxly. It is set to 2 on the first pass through the code; after the non-zero return from sigsetjmp(), it is set by user input. It is also likely to be clobbered by the setjmp() stuff because it isn't marked volatile. The rules are esoteric.

    However, the intention is that if begin is zero, then the program exits. Otherwise, it will continue. The sigprocmask() is intended to unblock any signals that were masked. It is not clear to me that it is necessary. By the time you return from the signal handler, the blocked signals should be unblocked — I think — even if you exit via siglongjmp().

Note that calling printf() in a signal handler invokes undefined behaviour. The chances are it will be OK, but it is not guaranteed. There is a list of functions that can be called, either in the POSIX standard or in other questions on SO (I know I've given that list before).


Read the manual pages for the functions carefully:

Chris Dodd is correct when he comments that sigsetjmp() with a second argument of 0 does not save the current signal mask. Note that the value of mask is in a local variable that is modified after setjmp() is called and is not marked volatile, so its value when setjmp() returns with a non-zero value is indeterminate (see the caveats in the setjmp() man page).

The rationale section of the man page for sigsetjmp() is interesting reading and mentions similar functionality appearing in 4.2 BSD (released in 1982), so my comment about them not existing in the 70s remains valid (K&R 1st edition and 7th Edition UNIX™ were released in 1978 and 1979 respectively). The sig* names were, AFAICT, invented by POSIX (the BSD systems included _setjmp() and _longjmp() instead).

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Are you answers true if we transported back in time to the 70s? – Fiddling Bits Dec 18 '13 at 01:23
  • 1
    To the 70s — the question is moot since functions like `sigsetjmp()` didn't exist at the time of K&R 1st Edition and 7th Edition UNIX™ (which is where I learned C and Unix; the system was a hybrid mostly 7th Edition with some System III enhancements, IIRC). Those functions appeared in the late 80s or early 90s as a result of POSIX standardization — the first version of POSIX was released in 1988. – Jonathan Leffler Dec 18 '13 at 01:27
  • And the `sigaction()` and related functions did not exist in the 70s either; nor did `sigprocmask()`. They too were a POSIX invention (rationalization). – Jonathan Leffler Dec 18 '13 at 01:33
  • @JonathanLeffler Sorry but one last question. So technically, this function runs, I type in a number, it starts to calculate, then if I send an exit signal, it will redirect me back to the sigsetjmp part and ask me to type in a number again? – jackhao Dec 18 '13 at 01:41
  • YW: You might find [What is the difference between `sigaction()` and `signal()`](http://stackoverflow.com/questions/231912/) and [How to avoid calling `printf()` in a signal handler](http://stackoverflow.com/questions/16891019/how-to-avoid-using-printf-in-a-signal-handler/16891799#16891799) of some use. – Jonathan Leffler Dec 18 '13 at 01:43
  • 1
    I'm not sure what you mean by an 'exit signal'…but the `sigsetjmp()` function may return two or more times. The first time it will return 0; the other times, it will return a non-zero value when the `siglongjmp()` call in `query()` is executed, which happens when a SIGQUIT signal is sent to the program. When `sigsetjmp()` returns with a non-zero value, the code will scan for a new value of `begin` before calling the signal handling code again and then finally starting to calculate perfect numbers from the newly specified starting point. – Jonathan Leffler Dec 18 '13 at 01:45
  • @JonathanLeffler I see. thanks. So will the scanf shows up first time I run the program? (It's not showing when i compile it in my computer). – jackhao Dec 18 '13 at 01:51
  • `setsetjmp` and `sigaction` predate POSIX by a fair amount -- I think they first showed up in BSD unix in the early 80s. If you call `sigsetjmp` with `0` as the second argument (as this code does), the current signal mask will NOT be saved, so the call to `siglongjmp` will NOT unblock the signals. Thus the call to `sigprocmask` to unblock them. – Chris Dodd Dec 18 '13 at 02:50
  • Thanks for the comments, @ChrisDodd. I've updated the answer in response to them. – Jonathan Leffler Dec 18 '13 at 04:04