一、 signal()函数介绍
signal函数是设置一个函数来处理信号,即带有 signum 参数的信号处理程序
linux下man signal如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
其中第一个参数signum – 在信号处理程序中作为变量使用的信号码。下面是一些重要的标准信号常量:
宏 | 信号 |
---|---|
SIGABRT | (Signal Abort) 程序异常终止。 |
SIGFPE | (Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算) |
SIGILL | (Signal Illegal Instruction) 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。 |
SIGINT | (Signal Interrupt) 中断信号,如 ctrl-C,通常由用户生成。 |
SIGSEGV | (Signal Segmentation Violation) 非法访问存储器,如访问不存在的内存单元。 |
SIGTERM | (Signal Terminate) 发送给本程序的终止请求信号。 |
第二个参数handler --一个指向函数的指针。它可以是一个由程序定义的函数,也可以是下面预定义函数之一
SIG_DFL | 默认的信号处理程序。 |
---|---|
SIG_IGN | 忽视信号。 |
返回值
该函数返回信号处理程序之前的值,当发生错误时返回 SIG_ERR。
/* by 面朝大海0902 */
二、应用层实例代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
static void sig_func(int sig)
{
int value;
read(fd, &value, 4);
printf("get button : 0x%x\n", value);
}
/*
* ./key-fasync-test /dev/my_key0
* by 面朝大海0902
*/
int main(int argc, char **argv)
{
int flags;
int ret;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
signal(SIGIO, sig_func);//注册信号回调函数
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
fcntl(fd, F_SETOWN, getpid());//告诉驱动往哪个pid上发送SIGIO信号
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);//触发调用驱动的fasync函数
while (1)
{
printf("by mianchaodahai 0902\n");
sleep(5);
}
close(fd);
return 0;
}
/* by 面朝大海0902 */
三、fcntl()函数
fcntl()函数是用来修改已经打开文件的属性的,linux下man fcntl如下:
fcntl - manipulate file descriptor
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
上面应用程序使用fcntl(fd, F_SETOWN, getpid())—实际设置的是用来接收SIGIO信号的进程id或者进程组id,告诉驱动往哪个pid上发送SIGIO信号。
应用层调用fcntl()对应内核会调用sys_fcntl()函数进而调用do_fcntl(),源码位于fs/fcntl.c里面(内核版本为4.9.88)
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
struct fd f = fdget_raw(fd);
long err = -EBADF;
if (!f.file)
goto out;
if (unlikely(f.file->f_mode & FMODE_PATH)) {
if (!check_fcntl_cmd(cmd))
goto out1;
}
err = security_file_fcntl(f.file, cmd, arg);
if (!err)
err = do_fcntl(fd, cmd, arg, f.file);
out1:
fdput(f);
out:
return err;
}
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
struct file *filp)
{
long err = -EINVAL;
switch (cmd) {
... ...
case F_SETFL:
err = setfl(fd, filp, arg);
break;
case F_SETOWN:
f_setown(filp, arg, 1);
err = 0;
break;
... ...
}
return err;
}
可以看到每个对应的cmd都会有各自的case去处理,F_SETOWN的处理函数最终调用到**f_modown()**函数设置文件属性的pid,type,uid,euid。
static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
int force)
{
write_lock_irq(&filp->f_owner.lock);
if (force || !filp->f_owner.pid) {
put_pid(filp->f_owner.pid);
filp->f_owner.pid = get_pid(pid);
filp->f_owner.pid_type = type;
if (pid) {
const struct cred *cred = current_cred();
filp->f_owner.uid = cred->uid;
filp->f_owner.euid = cred->euid;
}
}
write_unlock_irq(&filp->f_owner.lock);
}
F_SETFL的处理函数最终调用到**setfl()**函数,因为文件有应用层置的FASYNC标志位而调用到对应驱动文件的fasync()函数。
static int setfl(int fd, struct file * filp, unsigned long arg)
{
... ...
/*
* ->fasync() is responsible for setting the FASYNC bit.
*/
if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
if (error < 0)
goto out;
if (error > 0)
error = 0;
}
... ...
}
/* by 面朝大海0902 */
关于内核驱动如何向应用层发送信号,可以参考下一节Linux异步通知—fasync_helper()、kill_fasync()函数介绍与使用