Библиотека сайта rus-linux.net
Цилюрик О.И. Модули ядра Linux | ||
Назад | Вперед |
Сигналы
Точно так же, как запуск процесса, по аналогии с пользовательским пространством, можно посылать из ядра сигналы UNIX как пользовательским процессам, так и потокам пространства ядра. Для уяснения возможностей использования сигналов из ядра (и в ядре) я воспользовался несколько видоизменённым (архив signal.tgz) проектом из [6]. Идея теста проста:
- имеем пользовательское приложение sigreq («мишень» на которую направляются сигналы), и которое регистрирует полученные сигналы;
- имеем модуль ядра lab3_ioctl_signal.ko, которому можно «заказать»: какому процессу (PID) отсылать сигнал и какой сигнал, пользовательское приложение, в качестве целеуказания мы и будем указывать sigreq;
- и имеется диалоговый пользовательский процесс ioctl, который казывает модулю ядра: какой сигнал отсылать и кому.
ioctl.h :
#define MYIOC_TYPE 'k' #define MYIOC_SETPID _IO(MYIOC_TYPE,1) #define MYIOC_SETSIG _IO(MYIOC_TYPE,2) #define MYIOC_SENDSIG _IO(MYIOC_TYPE,3) #define SIGDEFAULT SIGKILL
- команды ioctl(), которые отрабатываются модулем: MYIOC_SETPID - установить PID процесса, которому будет направляться сигнал; MYIOC_SETSIG - установить номер отсылаемого сигнала; MYIOC_SENDSIG - отправить сигнал.
Собственно код модуля:
lab3_ioctl_signal.c :
#include <linux/module.h> #include "ioctl.h" #include "lab_miscdev.h" static int sig_pid = 0; static struct task_struct *sig_tsk = NULL; static int sig_tosend = SIGDEFAULT; static inline long mycdrv_unlocked_ioctl( struct file *fp, unsigned int cmd, unsigned long arg ) { int retval; switch( cmd ) { case MYIOC_SETPID: sig_pid = (int)arg; printk( KERN_INFO "Setting pid to send signals to, sigpid = %d\n", sig_pid ); /* sig_tsk = find_task_by_vpid (sig_pid); */ sig_tsk = pid_task( find_vpid( sig_pid ), PIDTYPE_PID ); break; case MYIOC_SETSIG: sig_tosend = (int)arg; printk( KERN_INFO "Setting signal to send as: %d \n", sig_tosend ); break; case MYIOC_SENDSIG: if( !sig_tsk ) { printk( KERN_INFO "You haven't set the pid; using current\n" ); sig_tsk = current; sig_pid = (int)current->pid; } printk( KERN_INFO "Sending signal %d to process ID %d\n", sig_tosend, sig_pid ); retval = send_sig( sig_tosend, sig_tsk, 0 ); printk( KERN_INFO "retval = %d\n", retval ); break; default: printk( KERN_INFO " got invalid case, CMD=%d\n", cmd ); return -EINVAL; } return 0; } static const struct file_operations mycdrv_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mycdrv_unlocked_ioctl, .open = mycdrv_generic_open, .release = mycdrv_generic_release }; module_init( my_generic_init ); module_exit( my_generic_exit ); MODULE_AUTHOR("Jerry Cooperstein"); MODULE_DESCRIPTION("LDD:1.0 s_13/lab3_ioctl_signal.c"); MODULE_LICENSE("GPL v2");
- в этом файле содержится интересующий нас обработчик функций ioctl(), все остальные операции модуля (создание символьного устройства /dev/mycdrv, open(), close(), ...) - отнесены во включаемый файл lab_miscdev.h, общий для многих примеров, и не представляющий интереса — всё это было подробно рассмотрено ранее, при рассмотрении операций символьного устройства. Пока остановим внимание на группе функций, находящих процесс по его PID, что близко смыкается с задачей запуска процесса, рассматриваемой выше:
#include <linux/sched.h> struct task_struct *find_task_by_vpid( pid_t nr ); #include <linux/pid.h> struct pid *find_vpid( int nr ); struct task_struct *pid_task( struct pid *pid, enum pid_type ); enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX };
Тестовая задача, выполняющая последовательность команда ioctl() над модулем: установку PID процесса, установку номера сигнала — отправку сигнала:
ioctl.c :
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/ioctl.h> #include <signal.h> #include "ioctl.h" static void sig_handler( int signo ) { printf( "---> signal %d\n", signo ); } int main( int argc, char *argv[] ) { int fd, rc; unsigned long pid, sig; char *nodename = "/dev/mycdrv"; pid = getpid(); sig = SIGDEFAULT; if( argc > 1 ) sig = atoi( argv[ 1 ] ); if( argc > 2 ) pid = atoi( argv[ 2 ] ); if( argc > 3 ) nodename = argv[ 3 ]; if( SIG_ERR == signal( sig, sig_handler ) ) printf( "set signal handler error\n" ); /* open the device node */ fd = open( nodename, O_RDWR ); printf( "I opened the device node, file descriptor = %d\n", fd ); /* send the IOCTL to set the PID */ rc = ioctl( fd, MYIOC_SETPID, pid ); printf("rc from ioctl setting pid is = %d\n", rc ); /* send the IOCTL to set the signal */ rc = ioctl( fd, MYIOC_SETSIG, sig ); printf("rc from ioctl setting signal is = %d\n", rc ); /* send the IOCTL to send the signal */ rc = ioctl( fd, MYIOC_SENDSIG, "anything" ); printf("rc from ioctl sending signal is = %d\n", rc ); /* ok go home */ close( fd ); printf( "FINISHED, TERMINATING NORMALLY\n"); exit( 0 ); }
Тестовая задача, являющаяся оконечным приёмником-регистраторов отправляемых сигналов:
sigreq.c :
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include "ioctl.h" static void sig_handler( int signo ) { printf( "---> signal %d\n", signo ); } int main( int argc, char *argv[] ) { unsigned long sig = SIGDEFAULT; printf( "my own PID is %d\n", getpid() );.. sig = SIGDEFAULT; if( argc > 1 ) sig = atoi( argv[ 1 ] ); if( SIG_ERR == signal( sig, sig_handler ) ) printf( "set signal handler error\n" ); while( 1 ) pause(); exit( 0 ); }
Примечание: В этом приложении (как и в предыдущем) для установки обработчика сигнала используется старая, так называемая «ненадёжная модель» обработки сигналов, использованием вызова signal(), но в данном случае это никак не влияет на достоверность получаемых результатов.
Начнём проверку с конца: просто с отправки процессу регистратору сигнала консольной командой kill, но прежде нужно уточниться с доступным в реализации нашей операционной системы набором сигналов (этот список для разных операционных систем может не очень значительно, но отличаться):
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
Для проверок функционирования выберем безобидный сигнал SIGUSR1(сигнал номер 10):
$ ./sigreq 10
my own PID is 10737 ---> signal 10
$ kill -n 10 10737
- вот как отреагировал процесс регистратор на получение сигнала. А теперь выполним весь комплекс: процесс ioctl последовательностью вызовов ioctl() заставляет загруженный модуль ядра отправить указанный сигнал процессу sigreq :
$ sudo insmod lab3_ioctl_signal.ko
$ lsmod | head -n2
Module Size Used by lab3_ioctl_signal 2053 0.
$ dmesg | tail -n2
Succeeded in registering character device mycdrv
$ cat /sys/devices/virtual/misc/mycdrv/dev
10:56
$ ls -l /dev | grep my
crw-rw---- 1 root root 10, 56 Май 6 17:15 mycdrv
$ ./ioctl 10 11684
I opened the device node, file descriptor = 3 rc from ioctl setting pid is = 0 rc from ioctl setting signal is = 0 rc from ioctl sending signal is = 0 FINISHED, TERMINATING NORMALLY
$ dmesg | tail -n14
Succeeded in registering character device mycdrv attempting to open device: mycdrv: MAJOR number = 10, MINOR number = 56 successfully open device: mycdrv: I have been opened 1 times since being loaded ref=1 Setting pid to send signals to, sigpid = 11684 Setting signal to send as: 10. Sending signal 10 to process ID 11684 retval = 0 closing character device: mycdrv:
$ ./sigreq 10
my own PID is 11684 ---> signal 10 ^C
Отправку сигнала в этой реализации осуществляет вызов send_sig(), он, и ещё большая группа функций, связанных с отправкой сигналов, определены в <linux/sched.h>, некоторые из которых:
int send_sig_info( int signal, struct siginfo *info, struct task_struct *task ); int send_sig( int signal, struct task_struct *task, int priv ); int kill_pid_info( int signal, struct siginfo *info, struct pid *pid ); int kill_pgrp( struct pid *pid, int signal, int priv ); int kill_pid( struct pid *pid, int signal, int priv ); int kill_proc_info( int signal, struct siginfo *info, pid_t pid );
Описания достаточно сложной структуры siginfo включено из заголовочных файлов пространства пользователя (/usr/include/asm-generic/siginfo.h):
typedef struct siginfo { int si_signo; int si_errno; int si_code; ... }
Тема сигналов чрезвычайно важная — на них основаны все механизмы асинхронных уведомлений, например, работа пользовательских API select() и pool(), или асинхронных операций ввода-вывода. Но тема сигналов и одна из самых слабо освещённых в литературе.
Предыдущий раздел: | Оглавление | Следующий раздел: |
Запуск процессов из ядра | Операции I/O пространства пользователя |