Safe use of the signal sem_wait () / sem_post ()

I am trying to create a shell in Linux that controls how many simultaneous executions of something are allowed at once. For this, I use a system counting semaphore. I create a semaphore, do sem_wait() , start the child process, and then do sem_post() when the child finishes work. This is normal.

The problem is how to safely process signals sent to this shell. If he doesn’t catch the signals, the command can end without executing sem_post() , as a result of which the number of semaphores will constantly decrease by one. So, I created a signal handler that does sem_post() . But still there is a problem.

If the handler is connected before the execution of sem_wait() , the signal may come before the completion of sem_wait() , as a result of which sem_post() will occur without sem_wait() . Reverse is possible if I do sem_wait() before setting up the signal handler.

The obvious next step was to block the signals while setting up the handler and sem_wait() . This is the pseudocode of what I have now:

 void handler(int sig) { sem_post(sem); exit(1); } ... sigprocmask(...); /* Block signals */ sigaction(...); /* Set signal handler */ sem_wait(sem); sigprocmask(...); /* Unblock signals */ RunChild(); sem_post(sem); exit(0); 

Now the problem is that sem_wait() can be blocked, and during this time the signals are blocked. A user trying to kill a process may end up resorting to "kill -9," whose behavior I do not want to encourage, since I cannot handle this matter no matter what. I could use sem_trywait() for a little while and the sigpending() test, but this affects fairness because there is no longer any guarantee that the process waiting for the semaphore, the longest, will be launched further.

Is there a really safe solution that allows me to process signals while receiving a semaphore? I am considering using a global “global merge” and removing signal blocking, but this is not 100% safe, since getting a semaphore and setting a global one is not atomic, but may be better than blocking signals while waiting.

+4
source share
3 answers

Are you sure sem_wait() forces you to block signals? I do not think so. The sem_wait() page says that the EINTR error code is returned from sem_wait() if it is interrupted by a signal.

You should be able to handle this error code, and then your signals will be received. Have you encountered a situation where signals were not received?

I would make sure that you handle the error codes that sem_wait() can return. Although this can be rare, if you want to be 100% sure that you want to cover 100% of your bases.

+6
source

Are you sure you approached the problem correctly? If you want to wait for the child to finish working, you can use the waitpid() system call. As you noticed, it is unreliable to expect a child to execute sem_post() if it can receive signals.

0
source

I know this is old, but for those still reading this courtesy of Google ...

The simplest (and only?) Reliable solution to this problem is to use the System V semaphore, which allows the client to acquire the semaphore resource in a way that is automatically returned by the kernel. NO MATTER AS AN EXIT PROCESS.

0
source

All Articles