全部博文(92)
分类: 嵌入式
2010-04-12 21:01:24
Linux设备驱动中的异步通知与异步I/O
Fcntl()必须先学习。
int fcntl(int fd, int cmd, ...);
[描述]
Fcntl()针对(文件)描述符提供控制.参数fd 是被参数cmd操作(如下面的描述)的描述符.
针对cmd的值,fcntl能够接受第三个参数int arg
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
cmd值:(我所用到的)
F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)
F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明,否则,arg将被认为是
F_GETFL 取得fd的文件状态标志,如同下面的描述一样(arg被忽略)
F_SETFL 设置给arg描述符状态标志,可以更改的几个标志是: O_APPEND, O_NONBLOCK,O_SYNC和O_ASYNC。
信号的接收:
为了在用户空间能处理一个设备释放的信号,它必须完成以下3项工作
l 通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。
l 通过F_SETFL IO控制命令设置文件支持FASYNC,即异步通知模式。
l 通过signal()函数连接信号和信号处理函数。
/*======================================================================
A test program to access /dev/second
This example is to help understand async IO
The initial developer of the original code is Baohua Song
======================================================================*/
#include
#include
#include
#include
#include
#include
/*接收到异步读信号后的动作*/
void input_handler(int signum)
{
printf("receive a signal from globalfifo,signalnum:%d\n",signum);
}
main()
{
int fd, oflags;
fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != - 1)
{
//启动信号驱动机制
signal(SIGIO, input_handler); //让input_handler()处理SIGIO信号
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
while(1)
{
sleep(100);
}
}
else
{
printf("device open failure\n");
}
}
信号的释放:
在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号时不够的,因为信号没有的源头在设备驱动端。因此,应该在适合的时机让设备驱动释放信号,在设备驱动中增加信号释放的相关代码。
为了使设备支持异步通知机制,驱动程序要做3项工作
l 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无需处理。
l 支持F_SETEL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数。
l 在设备资源可获得时,调用kill_fasync()函数激发响应的信号。
/*======================================================================
A globalfifo driver as an example of char device drivers
This example is to introduce asynchronous notifier
The initial developer of the original code is Baohua Song
======================================================================*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GLOBALFIFO_SIZE 0x1000 /*全局fifo最大4K字节*/
#define FIFO_CLEAR 0x1 /*清0全局内存的长度*/
#define GLOBALFIFO_MAJOR 253 /*预设的globalfifo的主设备号*/
static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifo设备结构体*/
struct globalfifo_dev
{
struct cdev cdev; /*cdev结构体*/
unsigned int current_len; /*fifo有效数据长度*/
unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/
struct semaphore sem; /*并发控制用的信号量*/
wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/
wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/
struct fasync_struct *async_queue; /* 异步结构体指针,用于读 */
};
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
int ret;
DECLARE_WAITQUEUE(wait, current); //定义等待队列
down(&dev->sem); //获取信号量
add_wait_queue(&dev->w_wait, &wait); //进入写等待队列头
/* 等待FIFO非满 */
if (dev->current_len == GLOBALFIFO_SIZE)
{
if (filp->f_flags &O_NONBLOCK)
//如果是非阻塞访问
{
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
up(&dev->sem);
schedule(); //调度其他进程执行
if (signal_pending(current))
//如果是因为信号唤醒
{
ret = - ERESTARTSYS;
goto out2;
}
down(&dev->sem); //获得信号量
}
/*从用户空间拷贝到内核空间*/
if (count > GLOBALFIFO_SIZE - dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
if (copy_from_user(dev->mem + dev->current_len, buf, count))
{
ret = - EFAULT;
goto out;
}
else
{
dev->current_len += count;
printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
->current_len);
wake_up_interruptible(&dev->r_wait); //唤醒读等待队列
/* 产生异步读信号 */
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
ret = count;
}
out: up(&dev->sem); //释放信号量
out2:remove_wait_queue(&dev->w_wait, &wait); //从附属的等待队列头移除
set_current_state(TASK_RUNNING);
return ret;
}
/*文件释放函数*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
/* 将文件从异步通知列表中删除 */
globalfifo_fasync( - 1, filp, 0);
return 0;
}