编写Linux驱动的过程中,需要考虑与应用程序交互的情况。当设备驱动完成某项任务或者达到某种状态的时候(如设备文件可以写入或读取),此时可以让驱动程序主动通知应用程序进行相应的处理(个人感觉类似于Android应用程序中的广播)。这种在Linux内核中使用的“广播”就是本文要详细介绍的“信号”。
1. Linux信号类型
使用信号进行进程间通信(IPC)是LInux系统的重要通信机制。在Linux系统中,异步通知使用信号来实现。以下是Linux系统支持的信号及其含义:(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 SIGPWR 30
#define SIGSYS 31
/* These should not be considered constants from userland. */
#define SIGRTMIN 32
#define SIGRTMAX _NSIG
#define SIGSWI 32
注:以上除了SISSTOP和SIGKILL两个信号外,进程可以忽略或捕获其他的全部信号。
如果其中一个信号被捕获,说明在应用程序中为该信号指定了一个处理函数。如果一个信号没有被某个进程捕获,Linux内核会对该信号采用默认处理方式进行处理。
2. 接收Linux信号
在用户程序中,为了捕捉信号,需要使用signal函数来设置信号接收的回调函数:
void (*signal(int signum, void (* handler)(int)))(int);
从signal函数原型上看有些复杂, 但主要有两个参数:signum和handler,signum表示信号代码,handler表示回调函数。
在应用程序中捕获信号比较简单,如下例子:
void signal_handler(int signo)
{
printf("捕获到%d信号\n",signo);
}
main()
{
printf("信号ID是%d\n",getpid());
signal(SIGINT, signal_handler); //设置SIGINT信号的处理函数
signal(SIGHUP, signal_handler); //设置SIGHUP信号的处理函数
signal(SIGQUIT, signal_handler); //设置SIGQUIT信号的处理函数
getchar();
}
以上代码示例了如何捕捉信号,并设置相应的处理函数。
3、发送信号
在设备驱动和应用程序的异步通知过程中,驱动程序需要向应用程序发出信号,为了完成发送信号的任务,需要完成以下工作:
- 支持F_SETDOWN命令。通过这个命令可以设置file_f_owner为对应进程的ID。
- 支持F_SETFL命令的处理。每当FASYNC标志改变时,驱动程序中的fasync函数就会执行。
- 再满足条件时,调用kill_fasync函数发送相应的信号。
接收信号可以用signal函数和sigaction函数来完成,他们之间有以下几个区别:
signal函数
1、signal在调用handler之前先把信号的handler指针恢复;sigaction调用之后不会恢复handler指针,直到再次调用sigaction修改handler指针。
:这样,(1)signal就会丢失信号,而且不能处理重复的信号,而sigaction就可以。因为signal在得到信号和调用handler之间有个时间把handler恢复了,这样再次接收到此信号就会执行默认的handler。(虽然有些调用,在handler的以开头再次置handler,这样只能保证丢信号的概率降低,但是不能保证所有的信号都能正确处理)
2、signal在调用过程不支持信号block;sigaction调用后在handler调用之前会把屏蔽信号(屏蔽信号中自动默认包含传送的该信号)加入信号中,handler调用后会自动恢复信号到原先的值。
(2)signal处理过程中就不能提供阻塞某些信号的功能,sigaction就可以阻指定的信号和本身处理的信号,直到handler处理结束。这样就可以阻塞本身处理的信号,到handler结束就可以再次接受重复的信号。
在驱动程序编写过程中与应用程序进行交互是非常重要的,他可以确保用户程序能够顺利执行,也能保证驱动能够更好地服务于用户。以上是本人学习中的一些总结,希望对大家有帮助~~