1 (26.12.2013 11:53:05 отредактировано marikk)

Всем привет. Помогите пожалуйста получить сигнал от сокета с дополнительной информацией.
Информация из man fcntl:

+ открыть спойлер

F_SETSIG (int)
              Set  the  signal  sent  when input or output becomes possible to the value given in arg.  A value of zero means to send the default SIGIO
              signal.  Any other value (including SIGIO) is the signal to send instead, and in this case additional info is  available  to  the  signal
              handler if installed with SA_SIGINFO.

              By  using  F_SETSIG  with  a nonzero value, and setting SA_SIGINFO for the signal handler (see sigaction(2)), extra information about I/O
              events is passed to the handler in a siginfo_t structure.  If the si_code field indicates the source is SI_SIGIO, the si_fd  field  gives
              the  file descriptor associated with the event.  Otherwise, there is no indication which file descriptors are pending, and you should use
              the usual mechanisms (select(2), poll(2), read(2) with O_NONBLOCK set etc.) to determine which file descriptors are available for I/O.

              By selecting a real time signal (value >= SIGRTMIN), multiple I/O events may be queued using the same signal numbers.  (Queuing is depen‐
              dent on available memory).  Extra information is available if SA_SIGINFO is set for the signal handler, as above.

              Note  that  Linux imposes a limit on the number of real-time signals that may be queued to a process (see getrlimit(2) and signal(7)) and
              if this limit is reached, then the kernel reverts to delivering SIGIO, and this signal is delivered to the entire process rather than  to
              a specific thread.

На русском

+ открыть спойлер

F_SETSIG
Установить сигнал, который будет послан, когда станет возможен ввод или вывол. Значение 0 означает сигнал SIGIO. Любое другое значение (включая SIGIO) является другим сигналам, посылаемым вместо SIGIO и в это случае, для обработчика сигнала доступна дополнительная информация, если он был установлен с SA_SIGINFO.
В случае использования F_SETSIG с ненулевым значением и установкой SA_SIGINFO для обработчика сигнала (см. sigaction(2)), обработчику передаётся дополнительная информация о событиях ввода/вывода в структуре siginfo_t. Если поле si_code показывает, что сигналом является SI_SIGIO, то поле si_fd содержит файловый дескриптор, ассоциированный с собрытием вывода/вывода. В противном случае, не существует никакого механизма, чтобы сообщить с каким файловым дескриптором связан полученный сигнал и вы должны использовать (select(2), poll(2), read(2) с установленным флагом O_NONBLOCK и т.д.) чтобы определить какой файловый дескриптор доступен для ввода/вывода.

При выборе сигнала реального времени (value >= SIGRTMIN) как описано в POSIX.1b, в очередь будут добавляться несколько событий ввода вывода, с теми же номерами сигналов. (Размер очереди зависит от доступной памяти). Дополнительная информация будет доступна как описано выше, если для обработчика сигнала будет установлено SA_SIGINFO.

Мои попытки

#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>

void hello (int signum, siginfo_t *info, void *f)
{
  if (info->si_signo == SIGIO)
    fprintf (stderr, "Сигнал получен от %i  \n", info->si_fd);
}

int main ()
{
  struct sigaction act;
  memset (&act, 0, sizeof(act));

  act.sa_sigaction = hello;
  act.sa_flags = SA_SIGINFO;
  sigaction (SIGIO, &act, NULL);

 int client = 0;
  struct sockaddr_in saddr;
  int sd = socket (AF_INET, SOCK_STREAM, 0);

  memset(&saddr, 0, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = INADDR_ANY;
  saddr.sin_port = htons(4000);

  if (bind(sd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0 ) {
    perror ("bind");
    close (sd);
    return 1;
  }

  if ( listen(sd, 20) != 0) {
    perror ("Listen");
    close (sd);
    return 1;
  }

  while (1) {
    int size = sizeof (saddr);
    memset (&saddr, 0, size);

    client = accept (sd, (struct sockaddr *)&saddr, &size);
    if (client > 0)  {
      int s_f;
      fcntl(client, F_SETOWN, getpid());
      fcntl(client, F_SETFL, FASYNC);
    }
    else { 
      perror ("Ошибка обработки входящего сокета");
      continue;
    }
  }
} 

После коннекта отправляю порцию данных. Сигнал успешно принимается распознается, но вот номер сокета выводится некорректно 205760316 или -588753092.
Так же не получается установить посылаемый сигнал с помощью  fcntl, F_SETSIG. Выдает ошибку (F_SETSIG undeclared (first use in this function)).
uname -a

Linux vmovs-admin 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

2

marikk пишет:

Так же не получается установить посылаемый сигнал с помощью  fcntl, F_SETSIG. Выдает ошибку (F_SETSIG undeclared (first use in this function)).

компилируй с -D_GNU_SOURCE
или задефайнь его до первого инклюда.

marikk пишет:

Сигнал успешно принимается распознается, но вот номер сокета выводится некорректно 205760316 или -588753092.

скорее всего это из-за отсутствия вызова fcntl().

Собственно зачем это всё? есть же poll/select.

95% процентов проблем находятся между клавиатурой и стулом.

3

Fat-Zer пишет:

Собственно зачем это всё?

Спортивный интерес.

Рабочий код

#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <openssl/md5.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
#include <postgresql/libpq-fe.h>
#include <signal.h>

void hello (int signum, siginfo_t *info, void *f)
{
  fprintf (stderr, "привет от %i\n", info->si_fd);
}

int main ()
{
  struct sigaction act;
  memset (&act, 0, sizeof(act));
 
  act.sa_sigaction = hello;
  act.sa_flags = SA_SIGINFO;
  sigaction (SIGUSR1, &act, NULL);

  int client = 0;
  struct sockaddr_in saddr;
  int sd = socket (AF_INET, SOCK_STREAM, 0);

  memset(&saddr, 0, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = INADDR_ANY;
  saddr.sin_port = htons(4000);

  if (bind(sd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0 ) {
    perror ("bind");
    close (sd);
    return 1;
  }
 
  while (1) {

    int size = sizeof (saddr);
    memset (&saddr, 0, size);

    client = accept (sd, (struct sockaddr *)&saddr, &size);
    if (client > 0)  {
      int s_f;
      fcntl(client, F_SETOWN, getpid());
      fcntl(client, F_SETFL, FASYNC);
      fcntl(client, F_SETSIG, SIGUSR1);
 
    }
    else {
      perror ("Ошибка обработки входящего сокета");
      continue;
    }
  }
}

Компиляция

 gcc test.c -D_GNU_SOURCE