异步通知

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问。这样,使用无阻塞 I/O 的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代。

 

1. 信号:

使用信号进行进程间通信(IPC)是 UNIX 中的一种传统机制,Linux 也支持这种机制。在Linux 中,异步通知使用信号来实现,Linux 中可用的信号如下:

信号 含义
SIGHUP 1 挂起
SIGINT(ctrl+c) 2 终端中断
SIGQUIT 3 终端退出
SIGILL 4 无效命令
SIGTRAP 5 跟踪陷阱
SIGIOT 6 IOT 陷阱
...    
SIGKILL 9 强行终止(不能被捕获或忽略)
SIGTERM 15 终止
SIGCHLD 17 子进程已经停止或退出
SIGSTOP 19 停止执行(不能被捕获或忽略)
SIGIO 29 I/O

 

除了 SIGSTOP 和 SIGKILL 两个信号外,进程能够忽略或捕获其他的全部信号。一个信号被捕获的意思是当一个信号到达时有相应的代码处理它。如果一个信号没有被这个进程所捕获,内核将采用默认行为处理。

 

2. 信号的接收:

用户程序中,定义信号的处理函数用如下函数:

 (*signal( signum,  (*handler)()))(); 

第一个参数,指定信号的值,就是上边 SIGCHLD 之类的宏的值

第二个参数,指定针对前边信号值的处理函数

    SIG_IGN,忽略信号

    SIG_DFL,系统默认的方式处理

用户自定义函数,信号捕捉后函数被执行

返回值:

成功,函数的返回值

失败,SIG_ERR

 

一个用 ctrl+c 触发到自己的函数中的例子:

 sigterm_handler( signo) 
 { 
    ("Have caught sig N.O. %d\n", signo); 
    exit(0); 
 } 
  
  () 
 { 
    signal(SIGINT, sigterm_handler); 
    while(1); 
  
    return 0; 
 } 

一个用实现输入字符,打印字符异步的例子:

 #include  
 #include  
 #include  
 #include  
 #include  
 #include  
 #define MAX_LEN 100 
  input_handler( num) 
 { 
   char data[MAX_LEN]; 
    len; 
  
   /* 读取并输出 STDIN_FILENO 上的输入 */ 
   len = read(STDIN_FILENO, &data, MAX_LEN); 
   data[len] = 0; 
   ("input available:%s\n", data); 
 } 
  
 () 
 { 
    oflags; 
  
   /* 启动信号驱动机制 */ 
   signal(SIGIO, input_handler); 
   fcntl(STDIN_FILENO, F_SETOWN, getpid()); 
   oflags = fcntl(STDIN_FILENO, F_GETFL); 
   fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); 
  
   /* 最后进入一个死循环,仅为保持进程不终止,如果程序中 
   没有这个死循会立即执行完毕 */ 
   while (1); 
 } 

程序解释:

主函数中:

①为 SIGIO 信号安装 input_handler()作为处理函数

②fcntl(STDIN_FILENO, F_SETOWN, getpid()); 设置本进程为STDIN_FILENO 文件的拥有者(owner)

③对设备设置 FASYNC 标志

运行结果:

> ./signal_test

I am Chinese.

input available: I am Chinese.

I love Linux driver.

input available: I love Linux driver.

需要注意的是:当用户输入完字符串的时候,才会触发 SIGIO 的信号

 

用户空间实现异步通知的 3 项工作:

  1. 通过 F_SETOWN IO 控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。 
  2. 通过 F_SETFL IO 控制命令设置设备文件支持 FASYNC,即异步通知模式。 
  3. 通过 signal()函数连接信号和信号处理函数。

3. 信号的释放:

在设备驱动和应用程序的异步通知交互中,需要应用程序捕捉信号,驱动程序释放信号。

 

所涉及的 3 项工作:

  1. 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无需处理。 
  2. 支持 F_SETFL 命令的处理,每当FASYNC 标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现 fasync()函数。 
  3. 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号。

可以看出,上述的3项工作和应用程序中的3项是对应的,他们之间的关系,如下图表现的很好:

异步通知_第1张图片

 

 

在设备驱动中异步编程主要涉及一个数据结构、两个函数

一个数据结构:

fasync_struct 不需要知道具体的内容,只需要作为一个参数传递给 fasync_helper 就可以了

两个函数:

处理 FASYNC 标志的变更

fasync_helper( fd, struct file *filp, mode, struct fasync_struct **fa);

    前三个参数,直接使用 static xxx_fasync( fd, struct file *filp, mode) 中的参数

    fa,由用户定义的 fasync_struct 的机构体的指针

释放信号

 

kill_fasync(struct fasync_struct **fa, sig, band);

    fa,由用户定义的 fasync_struct 的机构体的指针

    sig,信号的号 / 表征信号的宏

    band,POLL_IN,可读。POLL_OUT,可写

一般的格式和有关此部分的例子:

static xxx_fasync( fd, struct file *filp, mode)

{

    struct xxx_dev *dev = filp->private_data;

    return fasync_helper(fd, filp, mode, &dev->async_queue);

}

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,

loff_t *f_pos)

{

    struct xxx_dev *dev = filp->private_data;

    ...

    /* 产生异步读信号 */

    (dev->async_queue)

    kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

    ...

}

static xxx_release(struct inode *inode, struct file *filp)

{

    /* 将文件从异步通知列表中删除 */

    xxx_fasync(-1, filp, 0);

    ...

    return 0;

}

 

 

 

你可能感兴趣的:(c++编程,异步通知,驱动,linux)