I found SIGIO signal documented in GNU library. It is stated that there is a possibility the system to send a signal whenever I have input (particularly in socket).
According to documentation to create such signal I should set O_ASYNC flag to corresponding filedescritor.
My problem is that my GNU version (gcc 6.3.0) doesn't recognize such a keyword:
error: ‘O_ASYNC’ undeclared (first use in this function)
I used following block to set a flag:
/* Set socket status async */
int status = fcntl(sockfd, F_SETFL, O_ASYNC);;
if (status < 0) error("Can't set async mode");
else printf("Async is set for signal %d\n", SIGIO);
I use Cigwin GCC 6.3.0
The code is below:
static void OnTimer(int sig)
{
/* write request to socket BIO */
}
static void OnTick(int sig)
{
char read[BUFSIZE] = {};
int received;
received = SSL_read(ssl, read, sizeof(read));
/* Handle errors here */
/* print the server's reply */
printf("%s\n\n", read);
}
void connectSSL()
{
/* do all socket set-up and connection */
/* Establish handler for I/O signal */
saction.sa_flags = 0;
saction.sa_handler = OnTick;
sigemptyset(&saction.sa_mask);
sigaddset(&saction.sa_mask, SIGALRM);
if (sigaction(SIGIO, &saction, NULL) == -1) error("sigaction");
else printf("OnTick handler created\n");
/* Set socket status async */
int status = fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | FASYNC);
if (status < 0) error("Can't set async mode");
else printf("Async is set for signal %d\n", SIGIO);
/* Select the process to receive the signal */
int process = fcntl(sockfd, F_SETOWN, getpid());
if (process < 0) error("Can't set address process");
else printf("Process %d is set\n", getpid());
/* do the rest SSL staff here */
}
int main(int argc, char argv[])
{
sigset_t mask, oldmask;
/* Wait for OnTimer event*/
sigemptyset(&oldmask);
sigaddset(&oldmask, SIGALRM);
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigaddset(&mask, SIGIO);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
connectSSL();
createTimer(500000000);
while(1)
{
printf("\nWaiting OnTimer %d\n", count + 1);
sigsuspend(&oldmask);
}
return 0;
}
Most probably you forgot to include sys/fcntl.h, either indirectly or directly. This is the place that defines the flags.
In case O_ASYNC is not defined, you can try and use the functionally equivalent predecessor of that flag, FASYNC.
Also make sure you have executed a
int flags;
/* Install signal handler here... */
/* Set flags to receive SIGIO (and SIGURG) signals */
flags = fcntl (F_GETFL, 0);
flags = fcntl (F_SETFL, flags | FASYNC);
/* Bind our process as receiver of SIGIO signals */
flags = fcntl (F_SETOWN, getpid ());
Related
I'm a newbie in c development. Recently, I noticed a problem when I was learning multi-threaded development, when I set a signal in the main thread of Action and when I try to block the signal action set by the main thread in the child thread, I find that it does not work.
Here is a brief description of the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
void *thread_start(void *_arg) {
sleep(2);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
printf("child-thread executed\n");
while (true) {
sleep(1);
}
return NULL;
}
void sig_handler(int _sig) {
printf("executed\n");
}
int main(int argc, char *argv[]) {
pthread_t t_id;
int s = pthread_create(&t_id, NULL, thread_start, NULL);
if (s != 0) {
char *msg = strerror(s);
printf("%s\n", msg);
}
printf("main-thread executed, create [%lu]\n", t_id);
signal(SIGUSR2, sig_handler);
while (true) {
sleep(1);
}
return EXIT_SUCCESS;
}
The signal mask is a per-thread property, a thread will inherit whatever the parent has at time of thread creation but, after that, it controls its own copy.
In other words, blocking a signal in a thread only affects the delivery of signals for that thread, not for any other.
In any case, even if it were shared (it's not), you would have a potential race condition since you start the child thread before setting up the signal in the main thread. Hence it would be indeterminate as to whether the order was "parent sets up signal, then child blocks" or vice versa. But, as stated, that's irrelevant due to the thread-specific nature of the signal mask.
If you want a thread to control the signal mask of another thread, you will need to use some form of inter-thread communication to let the other thread do it itself.
As I wrote in a comment, any USR1 signal sent to the process will be delivered using the main thread. It's output will not tell you exactly what happened, so it is not really a good way to test threads and signal masks. Additionally, it uses printf() in a signal handler, which may or may not work: printf() is not an async-signal safe function, so it must not be used in a signal handler.
Here is a better example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* This function writes a message directly to standard error,
without using the stderr stream. This is async-signal safe.
Returns 0 if success, errno error code if an error occurs.
errno is kept unchanged. */
static int write_stderr(const char *msg)
{
const char *end = msg;
const int saved_errno = errno;
int retval = 0;
ssize_t n;
/* If msg is non-NULL, find the string-terminating '\0'. */
if (msg)
while (*end)
end++;
/* Write the message to standard error. */
while (msg < end) {
n = write(STDERR_FILENO, msg, (size_t)(end - msg));
if (n > 0) {
msg += n;
} else
if (n != 0) {
/* Bug, should not occur */
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
/* Paranoid check that exactly the message was written */
if (!retval)
if (msg != end)
retval = EIO;
errno = saved_errno;
return retval;
}
static volatile sig_atomic_t done = 0;
pthread_t main_thread;
pthread_t other_thread;
static void signal_handler(int signum)
{
const pthread_t id = pthread_self();
const char *thread = (id == main_thread) ? "Main thread" :
(id == other_thread) ? "Other thread" : "Unknown thread";
const char *event = (signum == SIGHUP) ? "HUP" :
(signum == SIGUSR1) ? "USR1" :
(signum == SIGINT) ? "INT" :
(signum == SIGTERM) ? "TERM" : "Unknown signal";
if (signum == SIGTERM || signum == SIGINT)
done = 1;
write_stderr(thread);
write_stderr(": ");
write_stderr(event);
write_stderr(".\n");
}
static int install_handler(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = signal_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
}
void *other(void *unused __attribute__((unused)))
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
while (!done)
sleep(1);
return NULL;
}
int main(void)
{
pthread_attr_t attrs;
sigset_t mask;
int result;
main_thread = pthread_self();
other_thread = pthread_self(); /* Just to initialize it to a sane value */
/* Install HUP, USR1, INT, and TERM signal handlers. */
if (install_handler(SIGHUP) ||
install_handler(SIGUSR1) ||
install_handler(SIGINT) ||
install_handler(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Create the other thread. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
result = pthread_create(&other_thread, &attrs, other, NULL);
pthread_attr_destroy(&attrs);
if (result) {
fprintf(stderr, "Cannot create a thread: %s.\n", strerror(result));
return EXIT_FAILURE;
}
/* This thread blocks SIGUSR1. */
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* Ready to handle signals. */
printf("Send a HUP, USR1, or TERM signal to process %d.\n", (int)getpid());
fflush(stdout);
while (!done)
sleep(1);
pthread_join(other_thread, NULL);
return EXIT_SUCCESS;
}
Save it as e.g. example.c, and compile and run using
gcc -Wall -O2 example.c -pthread -o exprog
./exprog
It will block the USR1 signal in the main thread, and HUP and TERM in the other thread. It will also catch the INT signal (Ctrl+C), which is not blocked in either thread. When you send it the INT or TERM signal, the program will exit.
If you send the program the USR1 signal, you'll see that it will always be delivered using the other thread.
If you send the program a HUP signal, you'll see that it will always be delivered using the main thread.
If you send the program a TERM signal, it too will be delivered using the main thread, but it will also cause the program to exit (nicely).
If you send the program an INT signal, it will be delivered using one of the threads. It depends on several factors whether you'll always see it being delivered using the same thread or not, but at least in theory, it can be delivered using either thread. This signal too will cause the program to exit (nicely).
I m trying to induce EINTR failure with semop call.
key_t semkey;
int semid;
struct sembuf sbuf;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
struct semid_ds ds;
/* Get unique key for semaphore. */
if ((semkey = ftok("/tmp", 'a')) == (key_t) -1) {
perror("IPC error: ftok"); exit(1);
}
/* Get semaphore ID associated with this key. */
if ((semid = semget(semkey, 0, 0)) == -1) {
/* Semaphore does not exist - Create. */
if ((semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | S_IRUSR |
S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != -1)
{
/* Initialize the semaphore. */
arg.val = 0;
sbuf.sem_num = 0;
sbuf.sem_op = 2; /* This is the number of runs without queuing. */
sbuf.sem_flg = 0;
if (semctl(semid, 0, SETVAL, arg) == -1
|| semop(semid, &sbuf, 1) == -1) {
perror("IPC error: semop"); exit(1);
}
}
else if (errno == EEXIST) {
if ((semid = semget(semkey, 0, 0)) == -1) {
perror("IPC error 1: semget"); exit(1);
}
goto check_init;
}
else {
perror("IPC error 2: semget"); exit(1);
}
}
else
{
/* Check that semid has completed initialization. */
/* An application can use a retry loop at this point rather than
exiting. */
check_init:
arg.buf = &ds;
if (semctl(semid, 0, IPC_STAT, arg) < 0) {
perror("IPC error 3: semctl"); exit(1);
}
if (ds.sem_otime == 0) {
perror("IPC error 4: semctl"); exit(1);
}
}
sbuf.sem_num = 0;
sbuf.sem_op = -1;
sbuf.sem_flg = SEM_UNDO;
while (semop(semid, &sbuf, 1) == -1)
{
if (errno != EINTR)
{
perror("IPC Error: semop"); exit(1);
break;
}
}
Most i get is Resource unavailable failure or Resource busy. I even tried multiple semaphores running in two different threads or two different processes. but i dint able to get EINTR failure. i even tried sending signal as SIGCHLD to the process when sometime semop is waiting for the semaphores.
As per zwol suggestion,
Here is what i tried but it still dint work, i mean i can't get EINTR.
int g_global_variable = 0;
void *sigusr1_block_thread (void *vargp)
{
while (1)
{
sleep (10);
printf ("sigusr1_block_thread\n");
}
return NULL;
}
void *semop_wait_thread (void *vargp)
{
int sem;
struct sembuf sops[2];
if((sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600))==-1){
return NULL;
}
if(semctl(sem,0,SETVAL,2)==-1){
exit(1);
}
sops[0].sem_num=0;
sops[0].sem_op=-1;
sops[0].sem_flg=0;
sops[1].sem_num=0;
sops[1].sem_op=0;
sops[1].sem_flg=0;
g_global_variable = 1;
printf ("Starting semop call \n");
if(eintr_check_semop(sem, sops,2)<0)
printf("Error semop\n");
return NULL;
}
int main()
{
pthread_t tid, tid1, tid2, tid3, tid4;
sigset_t set;
int s;
pthread_create(&tid, NULL, semop_wait_thread, NULL);
pthread_create(&tid2, NULL, semop_wait_thread, NULL);
pthread_create(&tid3, NULL, semop_wait_thread, NULL);
pthread_create(&tid4, NULL, semop_wait_thread, NULL);
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGCHLD);
s = pthread_sigmask(SIG_BLOCK, &set, NULL);
if (s != 0)
printf ("Error during pthread_sigmask");
pthread_create(&tid1, NULL, sigusr1_block_thread, NULL);
while (1)
{
sleep (1);
if (g_global_variable == 1)
{
sleep (10);
printf ("Send SIGUSR1/SIGCHLD signals \n");
/* Send signal */
pthread_kill( tid, SIGCHLD);
pthread_kill( tid2, SIGCHLD);
pthread_kill( tid3, SIGCHLD);
pthread_kill( tid4, SIGCHLD);
pthread_kill( tid1, SIGCHLD);
pthread_kill( tid1, SIGUSR1);
break;
}
else
continue;
}
pthread_join(tid, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_join(tid4, NULL);
return 0;
}
eintr_check_semop just a function in which semop error and return values are checked. if EINTR it prints the message saying same.
if i send sigusr1 to blocking thread (t, t2, t3, t4) semop call break and comes of the loop.
I didnt get EINTR by any means. Then i checked kernel source code.
https://elixir.bootlin.com/linux/latest/source/ipc/sem.c
During EINTR, i see they are looping and not reporting the same.
EINTR only happens when a process receives a signal while it's blocked on a blocking system call, and that signal has a handler, and that handler is configured to interrupt rather than restart system calls. (There are a few exceptions to this principle, but none of them involve semop.) Your program hasn't got any signal handlers, so EINTR won't happen, even if you do send it signals.
I can't tell you exactly how to do this off the top of my head, but the overall pattern that should work is:
Establish a signal handler for some signal. if you have no reason to pick some other specific signal, use SIGUSR1 . Use sigaction to do this, not signal, and do not include SA_RESTART in sa_flags. The handler doesn't have to do anything; it just has to exist.
If the program has more than one thread, use pthread_sigmask to block SIGUSR1 in every thread but one.
In the thread that has SIGUSR1 unblocked, perform a semop operation that will block (a "wait-for-zero" on a semaphore with a nonzero value, without IPC_NOWAIT).
After the above thread is definitely blocked on semop, from another thread within the program, use pthread_kill to send SIGUSR1 to the blocked thread. Or, from outside the program, use regular kill to send SIGUSR1 to the whole process; because the signal is unblocked in exactly one thread, that thread will receive the signal.
The hardest part is being sure that the thread is blocked on semop before the signal is sent. I'm not sure this is possible from inside the program, without a race condition.
I am learning how to use pselect. I took an example code which worked fine and modified it to call the same code from a thread which is spawned from main and it does not work (pselect remains blocked forever)
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
/* Flag that tells the daemon to exit. */
static volatile int exit_request = 0;
/* Signal handler. */
static void hdl (int sig)
{
exit_request = 1;
printf("sig=%d\n", sig);
}
/* Accept client on listening socket lfd and close the connection
* immediatelly. */
static void handle_client (int lfd)
{
int sock = accept (lfd, NULL, 0);
if (sock < 0) {
perror ("accept");
exit (1);
}
puts ("accepted client");
close (sock);
}
void *mythread(void *arg __attribute__ ((unused)))
{
int lfd;
struct sockaddr_in myaddr;
int yes = 1;
sigset_t mask;
sigset_t orig_mask;
struct sigaction act;
memset (&act, 0, sizeof(act));
act.sa_handler = hdl;
/* This server should shut down on SIGUSR1. */
if (sigaction(SIGUSR1, &act, 0)) {
perror ("sigaction");
return NULL;
}
sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("pthread_sigmask");
return NULL;
}
lfd = socket (AF_INET, SOCK_STREAM, 0);
if (lfd < 0) {
perror ("socket");
return NULL;
}
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(int)) == -1) {
perror ("setsockopt");
return NULL;
}
memset (&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons (10000);
if (bind(lfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
perror ("bind");
return NULL;
}
if (listen(lfd, 5) < 0) {
perror ("listen");
return NULL;
}
while (!exit_request) {
fd_set fds;
int res;
/* BANG! we can get SIGUSR1 at this point, but it will be
* delivered while we are in pselect(), because now
* we block SIGUSR1.
*/
FD_ZERO (&fds);
FD_SET (lfd, &fds);
res = pselect (lfd + 1, &fds, NULL, NULL, NULL, &orig_mask);
if (res < 0 && errno != EINTR) {
perror ("select");
return NULL;
}
else if (exit_request) {
puts ("exited");
break;
}
else if (res == 0)
continue;
if (FD_ISSET(lfd, &fds)) {
handle_client (lfd);
}
}
return NULL;
}
int main (int argc, char *argv[])
{
void * res;
pthread_t mythr_h;
pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
pthread_join(mythr_h, &res);
return 0;
}
strong text
After sending SIGUSR1 to this program I see that it remains blocked in the pselect call. When the code in mythread function is moved back into main and not spawning any thread from main, it works perfectly.
After sending SIGUSR1 to this program I see that it remains blocked in
the pselect call. When the code in mythread function is moved back
into main and not spawning any thread from main, it works perfectly.
That's to be expected -- there is no guarantee that a signal will be delivered to the "right" thread, since there is no well-defined notion of what the "right" thread would be.
Signals and multithreading don't mix particularly well, but if you want to do it, I suggest getting rid of the exit_request flag (note: the volatile keyword isn't sufficient to work reliably in multithreaded scenarios anyway), and instead create a connected pair of file descriptors (by calling either the pipe() function or the socketpair() function). All your signal handler function (hdl()) needs to do is write a byte into one of the two file descriptors. Have your thread include the other file descriptor in its read-socket-set (fds) so that when the byte is written that will cause pselect() to return and then your subsequent call to FD_ISSET(theSecondFileDescriptorOfThePair, &fds) will return true, which is how your thread will know it's time to exit now.
The signal is delivered to the main thread, other than the thread blocking on the pselect() call. If there are multiple threads that have the signal unblocked, the signal can be delivered to any one of the threads.
Since you didn't specify your platform, first I'm quoting from the POSIX standard (System Interfaces volume, 2.4.1 Signal Generation and Delivery).
Signals generated for the process shall be delivered to exactly one of those threads within the process which is in a call to a sigwait() function selecting that signal or has not blocked delivery of the signal.
You can also see similar statements in Linux manpage signal(7).
A process-directed signal may be delivered to any
one of the threads that does not currently have the signal blocked.
If more than one of the threads has the signal unblocked, then the
kernel chooses an arbitrary thread to which to deliver the signal.
And FreeBSD manpage sigaction(2).
For signals directed at the process, if the
signal is not currently blocked by all threads then it is delivered to
one thread that does not have it blocked (the selection of which is
unspecified).
So what you can do is to block SIGUSR1 for all the threads in the process except for the one that calls pselect(). Luckily when a new thread is created, it inherits the signal mask from its creator.
From the same POSIX section above,
The signal mask for a thread shall be initialized from that of its parent or creating thread....
Linux pthread_sigmask(3),
A new thread inherits a copy of its creator's signal mask.
FreeBSD sigaction(2),
The signal mask for a thread is initialized from that of its parent (normally empty).
You can make the following changes to your code. In main(), before creating any threads, block SIGUSR1. In the thread, pass a signal mask that has SIGUSR1 unblocked into pselect().
--- old.c Mon Mar 21 22:48:52 2016
+++ new.c Mon Mar 21 22:53:54 2016
## -56,14 +56,14 ##
return NULL;
}
- sigemptyset (&mask);
- sigaddset (&mask, SIGUSR1);
-
- if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
+ sigemptyset(&orig_mask);
+ if (pthread_sigmask(SIG_BLOCK, NULL, &orig_mask) < 0) {
perror ("pthread_sigmask");
return NULL;
}
+ sigdelset(&orig_mask, SIGUSR1);
+
lfd = socket (AF_INET, SOCK_STREAM, 0);
if (lfd < 0) {
perror ("socket");
## -126,6 +126,15 ##
{
void * res;
pthread_t mythr_h;
+ sigset_t mask;
+
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGUSR1);
+
+ if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) {
+ return 1;
+ }
+
pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
pthread_join(mythr_h, &res);
return 0;
Last thing is off topic. printf() is not an async-signal-safe function, so should not be called in the signal handler.
I want to use a virtual timer to pause a while loop till the timer expires. The goal is to have a function run periodically given a time interval.
I read on the gnu.org site that using pause() can be troublesome and they suggest using sigsuspend instead. I am using the code given in the example. My program looks as follows.
void handler_SIGALRM(int signo)
{
signo = 0; /* Get rid of warning "unused parameter ‘signo’" (in a portable way). */
puts(" ***********Cleared Alarm");
return;
}
int main(int argc, char *argv[]) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler_SIGALRM;
if (-1 == sigaction(SIGALRM, &sa, NULL ))
{
perror("sigaction() failed");
exit(EXIT_FAILURE);
}
struct itimerval it_val; /* for setting itimer */
it_val.it_value.tv_sec = 3; // INTERVAL/1000;
it_val.it_value.tv_usec = 0;//simData.misc
it_val.it_interval = it_val.it_value;
if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) {
perror("error calling setitimer()");
exit(1);
}
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask,SIGALRM);
/* Wait for a signal to arrive. */
sigprocmask(SIG_BLOCK, &mask, &oldmask);
sigsuspend(&oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
puts("unblocked");
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(1)
{
//...
sigsuspend(&oldmask);
//...
}
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return 0;
}
The code above accomplishes the "pause" that I need. However, when I run it embedded in another program (threaded). It just hangs, and just keeps calling _handler_SIGALRM_.
Am I doing something wrong? Why could my program be hanging?
in a single main() function,so need signal handling. Use Posix Message Queue IPC mechanism , can ignore the priority and other linked list message,to implement the scenario:
client:Knock Knock
server:who's there
client: pilcrow
Server:pilcrow,thanks a lot.
client:exit
all process terminated
stdin->POSIX MsgQ client send "knock knock" to server->Server compares string and send "who's there" back to client
What I got is :
client:knock knock
Server:Who's there?
client:pilcrow
pilcrow
client:Exit
Exit
1st round successfully give me the right result.From 2nd round, the client output the same typing on console.
Please help. Remember to use gcc -lrt to link mq_function.
Below is my code,
#include <mqueue.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#define MSG_SIZE 100 //max size of msg
#define MAX_MSG 1 //max # of msg
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
volatile sig_atomic_t mqflag; /* set nonzero by signal handler */
static void sig_usr1(int);
sigset_t zeromask, newmask, oldmask;
int main(int argc, char **argv) {
int c,flags;/* for getopt() */
pid_t child_pid;
mqd_t msgq_id;
struct mq_attr attr;
struct sigevent sigev;
char *buff_forward,*buff_backward;
flags=O_RDWR | O_CREAT;
attr.mq_msgsize=MSG_SIZE;
attr.mq_maxmsg=MAX_MSG;
buff_forward=malloc(attr.mq_msgsize);
buff_backward=malloc(attr.mq_msgsize);
while ((c= getopt(argc, argv, "e")) != -1) {
switch (c) {
case 'e': /* create the queue exclusive */
flags|= O_EXCL;
break;
}
}
if (optind!=argc-1){
printf("usage: [-e] <name>");
exit(1);
}
msgq_id = mq_open(argv[optind],flags,FILE_MODE,&attr);
/* producing the message */
mq_getattr(msgq_id, &attr) ;
printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- "
"large at most %ld bytes each\n\t- currently holds %ld messages\n",
argv[optind], attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
sigemptyset(&zeromask); /* no signals blocked */
sigemptyset(&newmask);
sigemptyset(&oldmask);
sigaddset(&newmask, SIGUSR1);
/* establish signal handler, enable notification */
signal(SIGUSR1, sig_usr1);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);/* block SIGUSR1 */
if ((child_pid=fork())==0){
for (; ; ) {
while (mqflag == 0)
sigsuspend(&zeromask);
mqflag =0; /* reset flag */
msgq_id=mq_open(argv[optind],O_RDONLY);
mq_receive(msgq_id, buff_forward, attr.mq_msgsize, NULL);
mq_close(msgq_id);
if (strcasecmp ("Knock Knock",buff_forward)==0){
strcpy(buff_backward,"Server:Who's there?");
}
else if(strcasecmp ("pilcrow", buff_forward)==0){
strcpy(buff_backward,"Server:Pilcrow,thanks a lot!");
}
else if(strcasecmp ("Exit",buff_forward)==0){
kill(getppid(),SIGTERM);
exit(0);
}
msgq_id=mq_open(argv[optind],O_WRONLY);
mq_send(msgq_id,buff_backward,MSG_SIZE,NULL);
mq_close(msgq_id);
mq_notify(msgq_id, &sigev); /* reregister */
}
sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* unblock SIGUSR1 */
exit(0);
}
else if(child_pid>0){
for(;;){
printf("client:");
gets(buff_forward);
msgq_id=mq_open(argv[optind],O_WRONLY);
mq_send(msgq_id,buff_forward,MSG_SIZE,NULL);
mq_close(msgq_id);
mq_notify(msgq_id, &sigev);
while(mqflag==0)
sigsuspend(&zeromask);
mqflag==0;
msgq_id=mq_open(argv[optind],O_RDONLY);
mq_receive(msgq_id, buff_backward, attr.mq_msgsize, NULL);
printf("%s\n",buff_backward);
mq_close(msgq_id);
}
sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* unblock SIGUSR1 */
exit(0);
}
return (EXIT_SUCCESS);
}
static void sig_usr1(int signo) {
mqflag = 1;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
return;
}
The child calls sigsuspend before calling mq_notify (because mq_flag is implicitly initialized to zero and the check for interruption is improperly performed before notification). It can never be woken up as intended.
Initialize mq_flag to 1 to see the difference. Then refactor.
UPDATED
The OP changed the code substantially, so I changed this answer, too.