O_ASYNC прекращает генерировать SIGIO - PullRequest
4 голосов
/ 12 августа 2011

Это немного долго ... Для начала я нахожусь на Linux 2.6.33, gcc 4.4.4.

Я написал небольшую программу, которая создает именованный канал и читает его, пока не увидит определенную строку, после чего избавится от FIFO и снова выполнит себя.

#include<unistd.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
int fifo;
#define put(x) write(1, x, (sizeof x)-1)
void reader(int a)
{
      char buf[26];
      int n;
      while((n=read(fifo, buf, 25))>0){
            buf[25] = '\0';
            if(!strncmp(buf, "moo", 3)){
                    put("exec()-ing\n");
                    close(fifo);
                    unlink("lefifo");
                    execl("/home/dave/a.out", "a.out", 0);
            }
            write(1, buf, n);
      }
}

main()
{
      signal(SIGIO, reader);
      mknod("lefifo", 0600|S_IFIFO,0);
      fifo = open("lefifo", O_RDONLY|O_NONBLOCK );
      fcntl(fifo, F_SETOWN, getpid());
      fcntl(fifo, F_SETFL, O_ASYNC);

      for(;;)
            pause();
}

После компиляции и запуска в фоновом режиме я могу отобразить lefifo, и он будет работать, как и ожидалось, пока не введу строку, начинающуюся с "moo" Следующий пример сеанса:

$ gcc fifo.c 
$ ./a.out&
$ echo klar > lefifo
klar
$ echo moo > lefifo
exec()-ing
$ echo klar2 > lefifo
$ echo where did you go > lefifo
$ echo moo > lefifo
$ pkill a.out

Генерирует этот след (немного жира):

execve("./a.out", ["./a.out"], [/* 36 vars */]) = 0
mknod("lefifo", S_IFIFO|0600)           = 0
open("lefifo", O_RDONLY|O_NONBLOCK)     = 3
getpid()                                = 3945
fcntl(3, F_SETOWN, 3945)                = 0
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC)     = 0
pause()                                 = ? ERESTARTNOHAND (To be restarted)
--- SIGIO (I/O possible) @ 0 (0) ---
read(3, "klar\n"..., 25)                = 5
write(1, "klar\n"..., 5)                = 5
read(3, ""..., 25)                      = 0
sigreturn()                             = ? (mask now [])
pause()                                 = ? ERESTARTNOHAND (To be restarted)
--- SIGIO (I/O possible) @ 0 (0) ---
read(3, "moo\n"..., 25)                 = 4
write(1, "exec()-ing\n"..., 13)         = 13
close(3)                                = 0
unlink("lefifo")                        = 0
execve("/home/dave/a.out", ["a.out"], [/* 36 vars */]) = 0
mknod("lefifo", S_IFIFO|0600)           = 0
open("lefifo", O_RDONLY|O_NONBLOCK)     = 3
getpid()                                = 3945
fcntl(3, F_SETOWN, 3945)                = 0
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC)     = 0
pause()                                 = ? ERESTARTNOHAND (To be restarted)
--- SIGTERM (Terminated) @ 0 (0) ---

Как видите, в первый раз нет проблем с созданием FIFO, и SIGIO генерируется просто отлично; но после exec() новый FIFO не будет генерировать никаких сигналов. Старый показывает успешное закрытие и, кажется, удаляется успешно.

Я весьма озадачен тем, почему он может так себя вести. Есть идеи?

Ответы [ 3 ]

5 голосов
/ 12 августа 2011

Когда вы устанавливаете обработчик сигнала с signal(), в конфигурации по умолчанию glibc выдаст семантику сигнала BSD: этот сигнал будет заблокирован во время выполнения обработчика сигнала и разблокирован при его возврате.

Когда вы вызываете exec() из обработчика сигнала, обработчик сигнала не возвращается, поэтому SIGIO остается заблокированным. Маска сигнала процесса наследуется на exec(), поэтому она остается заблокированной в новом экземпляре процесса.

Явно разблокируйте SIGIO, используя sigprocmask() в начале main(), и вы должны получить поведение, которое вам нужно.

3 голосов
/ 12 августа 2011

Когда вызывается обработчик сигнала, сигнал блокируется до тех пор, пока не будет выполнен обработчик сигнала. Как только обработчик сигнала завершится, вызывается sigreturn (), чтобы разблокировать сигнал.Это вы можете увидеть в своем рабочем случае.Но когда вы набираете 'moo', вы вызываете execl из обработчика сигнала, и execl () не вернется обратно, поэтому обработчик сигнала не вернется.Если обработчик сигнала не возвращается, sigreturn () не будет вызываться и сигнал не будет удален из списка блоков.

Вы можете увидеть состояние сигналов, используя cat / proc // status.Вы запускаете свою программу и видите / proc // status, там вы видите, что sigio находится в состоянии ожидания, а также он заблокирован.

Посетите www.rulingminds.com для статей по ядру Linux.

1 голос
/ 12 августа 2011

Я бы предложил, чтобы execl (или любая другая обработка) была удалена из обработчика сигнала.Вы можете установить флаг в обработчике сигналов и опросить его в себе main.

...