Чтобы помочь прояснить разные значения, которые может принимать si_code, рассмотрим пример, в котором SIGCHLD генерируется четырьмя разными способами: kill(), sigqueue(), raise() (использует системный вызов tkill()) и созданием дочернего процесса, который немедленно прерывается.
1: /* sicode.с */
2:
3: #include <sys/signal.h>
4: #include <stdlib.h>
5: #include <stdio.h>
6: #include <unistd.h>
7:
8: #ifndef SI_TKILL
9: #define SI_TKILL -6
10: #endif
11:
12: void handler(int signo, siginfo_t *info, void *f ) {
13: static int count = 0;
14:
15: printf('перехвачен сигнал, отправленный ');
16: switch(info->si_code) {
17: case SI_USER:
18: printf('kill()
'); break;
19: case SI_QUEUE:
20: printf('sigqueue()
'); break;
21: case SI_TKILL:
22: printf('tkill() или raise()
'); break;
23: case CLD_EXITED:
24: printf ('ядро сообщает, что дочерний процесс завершен
'); exit(0);
25: }
26:
27: if (++count == 4) exit(1);
28: }
29:
30: int main() {
31: struct sigaction act;
32: union sigval val;
33: pid_t pid = getpid();
34:
35: val.sival_int = 1234;
36:
37: act.sa_sigaction = handler;
38: sigemptyset(&act.sa_mask);
39: act.sa_flags = SA_SIGINFO;
40: sigaction(SIGCHLD, &act, NULL);
41:
42: kill(pid, SIGCHLD);
43: sigqueue(pid, SIGCHLD, val);
44: raise(SIGCHLD);
45:
46: /* Чтобы получить SIGCHLD от ядра, мы создаем дочерний процесс
47: и немедленно завершаем его. Обработчик сигнала выйдет после
48: получения сигнала от ядра, поэтому мы просто засыпаем
49: на время и позволяем программе прерваться подобным образом. */
50:
51: if (!fork()) exit(0);
52: sleep(60);
53:
54: return 0;
55: }
Если si_code равно SI_USER, SI_QUEUE или SI_TKILL, то доступны два дополнительных члена siginfo_t: si_pid и si_uid, которые представляют идентификатор процесса, пославшего сигнал и действительный идентификатор пользователя этого процесса.
Когда ядром посылается SIGCHLD, доступны члены si_pid, si_status, si_utime и si_stime. Первый из них, si_pid, задает идентификатор процесса, состояние которого изменилось[72]. Информация о новом состоянии доступна как в si_code (как показано в табл. 12.3) и в si_status, что идентично целому значению состояния, возвращаемому семейством функций wait().
Последние два члена, si_utime и si_stime, определяют период времени, которое потрачено дочерним приложением на работу в пользовательском режиме и в режиме ядра, соответственно (это подобно тому, что возвращают вызовы wait3() и wait4() в структуре struct rusage). Это время измеряется в тиках часов, заданных целым числом. Количество тиков в секунду задает макрос _SC_CLK_TCK, определенный в <sysconf.h>.
SIGSEGV, SIGBUS, SIGILL и SIGFPE — все они представляют si_addr, специфицирующий адрес, который вызвал сбой, описанный si code.
Ниже приведен простой пример проверки контекста сигнала. Он устанавливает обработчик сигнала для SIGSEGV, который печатает контекст сигнала и прерывает процесс. Нарушение сегментации генерируется попыткой обращения к NULL.
1: /* catch-segv.c */
2:
3: #include <sys/signal.h>
4: #include <stdlib.h>
5: #include <stdio.h>
6:
7: void handler(int signo, siginfo_t *info, void *f) {
8: printf('перехват');
9: if (info->si_signo == SIGSEGV)
10: printf('segv accessing %p', info->si_addr);
11: if (info->si_code == SEGV_MAPERR)
12: printf('SEGV_MAPERR');
13: printf('
');
14:
15: exit(1);
16: }
17:
