PDA

View Full Version : queued signals problem, C programming related



Unregistered
01-04-2002, 05:33 PM
Hi. I have been trying to make a program work, that is using realtime queued signals, but I had problems with it. What I am trying to do here is to check if all signals generated by the first source (program) are delivered to the second source (program). If you compile it and execute it you will see that only 2 of them are normally delivered, everything else is lost. I just can't find out what is wrong.
The programs are pretty straight forward. Thanks for your help in advance.

I am using:
Linux Slackware 8.0
kernel 2.4.17
gcc 2.95.3 20010315



/* ================================ SOURCE 1 ======================================*/

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

void main(int argc, char *argv[])
{
int pid;
int signo = SIGUSR1;

union sigval qval1;
union sigval qval2;
union sigval qval3;
union sigval qval4;
union sigval qval5;

qval1.sival_int = 1;
qval2.sival_int = 2;
qval3.sival_int = 3;
qval4.sival_int = 4;
qval5.sival_int = 5;

if (argc != 2) {
fprintf(stderr, "Usage: %s pid signal value\n", argv[0]);
exit(1);
}

pid = atoi(argv[1]);

fprintf(stderr, "Sending signal to process %d\n", pid);

sigqueue(pid, signo, qval1);
sigqueue(pid, signo, qval2);
sigqueue(pid, signo, qval3);
sigqueue(pid, signo, qval4);
sigqueue(pid, signo, qval5);

pause();
}




/* ================================ SOURCE 2 ======================================*/

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

void my_handler(int signo, siginfo_t* info, void *context)
{
sleep(2);
cout<<"SIGNAL HANDLER\n";
}

void main(void)
{
int pid = getpid();
struct sigaction act;
sigset_t sigset;

fprintf(stderr, "Process ID is %ld\n", (long)getpid());

sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
sigprocmask(SIG_BLOCK, &sigset, NULL);

sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = my_handler;
if (sigaction(SIGUSR1, &act, NULL) < 0) {
perror("Sigaction failed");
exit(1);
}

fprintf(stderr, "Signal SIGUSR1 = %d ready\n", SIGUSR1);

for( ; ; )
pause();
}

Engineer
01-04-2002, 07:13 PM
I don't really have the time to debug your code, but after looking at it I can say the following:

1) You are blocking all the signals except SIGALRM:

sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
sigprocmask(SIG_BLOCK, &sigset, NULL);

2) You are setting up a signal handler for only one signal:

if (sigaction(SIGUSR1, &act, NULL) < 0) {

3) In your signal handler you might be sleeping() for too long. Signals in UNIX are asynchronous, and they arrive pretty fast (much faster than 1 signal in 2 seconds) and even if the program is sleeping they will interrupt it.

Those are just quick tips. Hope this helped...

Unregistered
01-04-2002, 07:24 PM
that's exackly the porpuse of the sleep(). I must wait, so that the signal arrives in the mean-while. When a handler is executing the signal for this handler is blocked, until the handler returns. This means that by using queued signals the signal should be queued and arrive just after the termination of the previous signal handler. But this doesn't happen....

rook_5150
01-22-2002, 12:30 AM
Ok, here is my theory on why linux is only coming up with 2 of you signals from sigqueue.
(First of all, I don't think sleeping is a good idea in a signal handler,
you probably don't want to do much. [On a side note all signals were delivered
on a Solaris 8-Intel box, I tested your code on.])

Before I actually state my theory, here is my tracking through glibc and kernel source
code to where sigqueue actually starts doing work.

- sigqueue can be found in the glibc source code under sysdeps/unix/sysv/linux/sigqueue.c.
- this calls rt_sigqueueinfo (found in linux source kernel/signal.c
- which calls kill_proc_info
- which calls send_sig_info (here we are doing some real work, at least
interesting work)

I know here that this function is returning a 0 in your program, because I modified your source
code to check the return value of sigqueue, (and it is returning 0); send_sig_info is using the
variable "ret" for the return value, and you can see it doesn't get set to 0 until after a couple of
checks. The next possible return is if the signal is 0 or NULL, which in our case it isn't. The next
possible return with 0 is if the ignored_signal function returns non-zero. I tracked this through signal_type,
which shows that global sigals (kill and other special stuff), is not ignored "I could be wrong, you can
check the source it's also in signal.c"). Then we hit this section of code.



/* Support queueing exactly one non-rt signal, so that we
can get more detailed information about the cause of
the signal. */
if (sig < SIGRTMIN && sigismember(&t->pending.signal, sig))
goto out;


You can check to see that normal signals like SIGUSR1, SIGINT, etc.. are less than SIGRTMIN.
Check /usr/include/asm/signal.h (shown below). So if sigismember(&t->pending.signal, sig) returns non-zero,
you have a call to sigqueue that returns 0, which is never delivered at:



ret = deliver_signal(sig, info, t);


Now check the man page for sigismember:
sigismember tests whether signum is a member of set.
sigismember returns 1 if signum is a member of set, 0 if signum is
not a member, and -1 on error.

So you send one signal USR1 (and you are sleeping now in the handler), so the second signal gets queued
in &t->pending.signal (t is the task struct obtained from the pid), and finally the
rest of the signals of the same type get ignored, skipping the call to deliver_signal.



**** IN OTHER WORDS DON"T SLEEP IN SIGNALS AND DON'T EXPECT SIGNALS TO ALWAYS BE
DELIVERED. SIGNALS ARE LIKE UDP NOT TCP. ****

I could have this completly wrong, I'm not a kernel programmer for linux, so if there are any out
there, is this correct?




(you have got to love an os you can track this kind of stuff down in. I have had problems
determining the nature of signals, and posix queues in solaris for quite awhile, I wish I
had the source.)


[THE REST IS THE LINUX/GLIBC SOURCE CODE (under gnu license) I looked at]





./sysdeps/unix/sysv/linux/sigqueue.c [glibc source code]
---------------------------------------------------------------

extern int __syscall_rt_sigqueueinfo (int, int, siginfo_t *__unbounded);

#ifdef __NR_rt_sigqueueinfo
/* Return any pending signal or wait for one for the given time. */
int
__sigqueue (pid, sig, val)
pid_t pid;
int sig;
const union sigval val;
{
siginfo_t info;

/* First, clear the siginfo_t structure, so that we don't pass our
stack content to other tasks. */
memset (&info, 0, sizeof (siginfo_t));
/* We must pass the information about the data in a siginfo_t value. */
info.si_signo = sig;
info.si_code = SI_QUEUE;
info.si_pid = __getpid ();
info.si_uid = __getuid ();
info.si_value = val;

return INLINE_SYSCALL (rt_sigqueueinfo, 3, pid, sig, __ptrvalue (&info));
}
weak_alias (__sigqueue, sigqueue)



kernel/signal.c [linux source code]
-------------------------------------------------
asmlinkage long
sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo)
{
siginfo_t info;

if (copy_from_user(&info, uinfo, sizeof(siginfo_t)))
return -EFAULT;

/* Not even root can pretend to send signals from the kernel.
Nor can they impersonate a kill(), which adds source info. */
if (info.si_code >= 0)
return -EPERM;
info.si_signo = sig;

/* POSIX.1b doesn't mention process groups. */
return kill_proc_info(sig, &info, pid);
}


kernel/signal.c [linux source code]
-------------------------------------------------
inline int
kill_proc_info(int sig, struct siginfo *info, pid_t pid)
{
int error;
struct task_struct *p;

read_lock(&tasklist_lock);
p = find_task_by_pid(pid);
error = -ESRCH;
if (p)
error = send_sig_info(sig, info, p);
read_unlock(&tasklist_lock);
return error;
}


int
send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
unsigned long flags;
int ret;


#if DEBUG_SIG
printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
#endif

ret = -EINVAL;
if (sig < 0 || sig > _NSIG)
goto out_nolock;
/* The somewhat baroque permissions check... */
ret = -EPERM;
if (bad_signal(sig, info, t))
goto out_nolock;

/* The null signal is a permissions and process existance probe.
No signal is actually delivered. Same goes for zombies. */
ret = 0;
if (!sig || !t->sig)
goto out_nolock;

spin_lock_irqsave(&t->sigmask_lock, flags);
handle_stop_signal(sig, t);

/* Optimize away the signal, if it's a signal that can be
handled immediately (ie non-blocked and untraced) and
that is ignored (either explicitly or by default). */

if (ignored_signal(sig, t))
goto out;

/* Support queueing exactly one non-rt signal, so that we
can get more detailed information about the cause of
the signal. */
if (sig < SIGRTMIN && sigismember(&t->pending.signal, sig))
goto out;

ret = deliver_signal(sig, info, t);
out:
spin_unlock_irqrestore(&t->sigmask_lock, flags);
if ((t->state & TASK_INTERRUPTIBLE) && signal_pending(t))
wake_up_process(t);
out_nolock:
#if DEBUG_SIG
printk(" %d -> %d\n", signal_pending(t), ret);
#endif

return ret;
}


from /usr/include/asm/signal.h
------------------------------------------------------------------
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
/*
#define SIGLOST 29
*/
#define SIGPWR 30
#define SIGSYS 31
#define SIGUNUSED 31

/* These should not be considered constants from userland. */
#define SIGRTMIN 32
#define SIGRTMAX (_NSIG-1)