0

I'm brand new to c programming and am struggling with an assignment. The basics of the code is to have a 1 second timer (I'm using sleep(1)) that counts up starting with an increment of 1. Every time I send sigINT, I increase the increment by 1 and the counter prints off the new number. I use sigterm to decrement the number. All of this is working fine.

The last part is to kill the program if I use sigINT twice in one second. I have tried doing this with a "flag" which for me is set to 0. When the sigINT is called, increase it by 1. Have it reset to 0 in my while loop. I tried writing an if statement that if the increment is more than 1, the program would stop. But with sleep(0) and flag=0 right after that, I can never get the flag to be more than 1. I know this probably isn't the best way to do this, but have had no luck in finding a better solution. Any help is appreciated. Thanks.

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int inc = 1;
int flag = 0;

void handle_sigint(int sig_num)
{
    signal(SIGINT, handle_sigint);
    if (inc < 10)
    {
        inc++;
    }
    flag++;
    printf("\nflag=%d\n", flag);
    printf("\nSIGINT received: Increment is now %d\n", inc);
    fflush(stdout);
}

void handle_sigterm(int sig_num)
{
    signal(SIGTERM, handle_sigterm);
    if (inc > 0)
    {
        inc--;
    }
    printf("\nSIGTERM received: Increment is now %d\n", inc);
    fflush(stdout);
}

int main()
{
    int num = 0;
    signal(SIGINT, handle_sigint);
    signal(SIGTERM, handle_sigterm);
    printf("Process ID: %d\n", getpid());
    while (1)
    {
        if (flag > 1)
        {
            pid_t iPid = getpid();
            printf("\nExiting the program now\n"); 
            kill(iPid, SIGKILL);
        }   
        printf("%d\n", num);
        num = num + inc;
        sleep(1);
        flag = 0;
    }
    return 0;
}
the busybee
  • 10,755
  • 3
  • 13
  • 30
jtro
  • 1
  • 1
  • Please indent your code. Well, if you set `flag=0` __right after__ `sleep(1)`, then in `if(flag>1)` the `flag` will never be 1. You would have to send two SIGINT between setting `flag=0` and checking it. – KamilCuk Feb 27 '20 at 03:06
  • 2
    One possibility is to use a high-resolution timer like `clock_gettime()` in the signal handler, and keep the time of the last interrupt somewhere, and compare the new time and the old time to see whether the difference is less than 1 second (remembering to save the new time in the old time if not). Since you're already abusing the rules about what's [allowed in a signal handler](https://stackoverflow.com/q/16891019/15168), that won't cause any extra grief. Indeed, `clock_gettime() ` is explicitly allowed, whereas `printf()` and `fflush()` are not. – Jonathan Leffler Feb 27 '20 at 03:21
  • suggest using `setitimer()` and related to generate the 1 second intervals – user3629249 Feb 27 '20 at 03:42
  • OT: for ease of readability and understanding: 1) please consistently indent the code. Indent after every opening brace '{'. Unindent before every closing brace '}'. Suggest each indent level be 4 spaces. 2) separate code blocks: `for` `if` `else` `while` `do...while` `switch` `case` `default` via a single blank line – user3629249 Feb 27 '20 at 03:45
  • regarding: `sleep(1); flag=0;` These are in the wrong order for the stated objective. suggest placing the `flag=0;` before the call to `sleep()` – user3629249 Feb 27 '20 at 03:46
  • regarding: `pid_t iPid = getpid(); printf("\nExiting the program now\n"); kill(iPid, SIGKILL);` Suggest a simple: `exit( EXIT_FAILURE );` rather than those three statements – user3629249 Feb 27 '20 at 03:48

1 Answers1

1

If are setting flag=0 right after sleep(1) and right before checking if(flag>1). That means there is no time between setting it to zero and checking, and in that time you want to send to SIGINT signals to your program. You have set the flag to zero at best right after checking it.

sleep() call is interruptible! If sleep() is interrupted by the signal, it returns the number of seconds left to sleep. For reference read man sleep. As "1 second" is not really divisible by a smaller chunk, you could use usleep or just use nanosleep call. nanosleep returns -1 and sets errno to EINTR if signal was received during sleep. Because it returns the time left to sleep in the second argument, it's easy to do a loop with it. You have to add #include <errno.h> for errno and #include <time.h> for nanosleep.

So something along:

while (1) {
   if (flag > 1) {
          // some code
   }
   // printf("something");

   // set flag before sleeping!
   flag = 0; 

   // wait a second
   struct timespec rqtp = { 1, 0 };
   while (nanosleep(&rqtp, &rqtp) == -1 && errno == EINTR) {
       // repeat until we slept a full second
   }

}

Notes:

  • Good code! Please follow some indentation style and indent your code. I recommend linux kernel coding style.
  • printf and fflush are not signal-safe function. Calling them from a signal handler is undefined behavior. You should not call printf from a signal handler.
  • Technically volatile sig_atomic_t type should be used to synchronize with a signal handler. I guess it would be preferable to mark your flag and inc variables at least as volatile.
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thank you so much! This worked perfectly. I notated your comments on other improvements as well. – jtro Mar 09 '20 at 00:49