您的位置 首页 系统

arm驱动linux异步告诉与异步IO

《[arm驱动]linux异步通知与异步IO》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动…

《[arm驱动]linux异步告知异步IO》触及内核驱动函数二个,内核结构体一个,剖析了内核驱动函数二个;可参阅的相关应用程序模板或内核驱动模板二个,可参阅的相关应用程序模板或内核驱动三个

描绘:设备文件IO拜访:堵塞与非堵塞io拜访,poll函数供给较好的处理设备拜访的机制,可是假如有了异步告知整套机制就愈加完好了

一、堵塞 I/O,非堵塞IO,异步I/O

1、堵塞 I/O :挂起进程一向等候设备可拜访后再拜访

2、非堵塞IO:进程进行对设备拜访一次,不行拜访时,持续碑文下一条指令
3、异步I/O:十分类似于硬件上“中止”的概念(硬件去call软件,内核去call应用程序);信号是在软件层次上对中止机制的一种模仿;

a)原理:信号是异步的,一个进程不用经过任何操作来等候信号的抵达;事实上:进程也不知道信号究竟什么时候抵达;“一个进程收到一个异步告知信号”与”处理器收到一个中止恳求”原理是相同的;

4、异步I/O告知行列(async_queue):内核经过“内核异步告知的程序 fasync()函数”将设备文件fd描绘符参加异步告知行列(内核异步告知的链表)。当fd有I/O操作发生时内核经过kill_fasync()开释(发生) SIGIO 信号,然后到达主动告知注册过SIG_IO信号的应用程序。

5、异步告知方针:首要它是设备文件,其非必须注册过fasync()函的文件;异步告知方针不是不是一般文件(不是随意的/tmp/text.txt),咱们一般文件没有在内核中完结fasync()函数和kill_fasync()
二、异步通讯应用程序部分
模板一)设备文件的异步告知应用程序

voidinput_handler(intnum){//信号处理函数
}
//翻开方针设备
fd = open(“设备文件途径如/dev/xxx”, O_RDWR);
//设置好方针设备的SIGIO信号处理程序;等候内核kill_fasync()开释 SIGIO 信号
signal(SIGIO,input_handler);
//使当时进程变成文件的主人,这样才能使文件中的信号发到当时进程
fcntl(fd, F_SETOWN, getpid());
//取得当时fd的flag值
oflags = fcntl(fd, F_GETFL);
/*设置设备文件描绘符号fd的FASYNC异步告知标志,
即给fd添加异步告知形式,fasync()函数将fd参加异步IO告知行列*/
fcntl(fd, F_SETFL, oflags | FASYNC);

图示一、异步告知作业进程图

实例一)以规范输入输出设备异步告知

#include
#include
#include
#include
#include
#define MAX_LEN 100
voidinput_handler(intnum)
{
chardata[MAX_LEN];
intlen;
len = read(STDIN_FILENO, &data, MAX_LEN);
data[len] = 0;
printf(“input available :%s\n”, data);
}
voidsetFdAsync(intfd){
intoflags;
//当时进程变成文件的主人
fcntl(fd, F_SETOWN, getpid());
//本程序中fd = STDIN_FILENO规范输入设备设备文件描绘符号;一般文件内核中没有完结FASYNC,不能运用异步告知
oflags = fcntl(fd, F_GETFL);//
//FASYNC在glibc 的fcntl.h文件中能够看到这样的界说 #define FASYNC O_ASYNC
fcntl(fd, F_SETFL, oflags | FASYNC);
}
voidmain(){
intfd = STDIN_FILENO;//STDIN_FILENO输入输出设备描绘符号,一般是键盘
signal(SIGIO,input_handler);//设置好方针设备的SIGIO信号处理程序;等候内核kill_fasync()开释 SIGIO 信号
setFdAsync(fd);
while(1);
}

运转成果:

efgwrfgregr
input available :efgwrfgregr
sfsdf
input available :sfsdf
//本程序电脑上运转时,咱们体系对STDIN_FILENO有特别维护,while晒干的程序运转了两次,进程就被体系挂机休眠,此刻cpu耗费为0;
//但我在arm开发板上的linux2.6内核运转时,while正常,进程不被挂起,估量是没键盘的原因…,也待解

三、驱动程序部分
驱动程序:一项数据结构和两个函数
结构体一)一项数据结构—– fasync_struct结构体
内核源码一)fasync_struct结构体内核源码

struct fasync_struct {
int magic;//启用设备文件镜像,监听文件是否改变(这个说法我猜的)
int fa_fd;//文件描绘符
struct fasync_struct *fa_next; /* 异步告知单链表 */
//filp是进程经过PCB中的文件描绘符表找到该fd所指向的文件指针;在fopen流操作中运用file结构体指针它的长处是带有I/O缓存
struct file *fa_file;
//struct file一共该进程翻开的文件,其中有一个owner特点,用来一共翻开设备文件的进程
};

两个函数
内核部分函数一)fasync_helper处理设备文件异步告知的标志(O_ASYNC或FASYNC),将fd参加异步告知行列函数

fasync_helper(int fd, struct file * filp, int on, struct fasync_struct * * fapp);

内核源码二)fasync_helper内核源码剖析

//第一次咱们on = MODE = oflag | FASYNC,on!=0所以碑文if (on)对struct fasync_struct **fapp进行初始化,
//当程序开释设备运用myfasync_drv_fasync(-1, file, 0),就碑文goto out开释中止
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
struct fasync_struct *new = NULL;
int result = 0;
if (on) {//第一次分配fapp空间
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
write_lock_irq(&fasync_lock);
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {//第一次初始化fapp
if (fa->fa_file == filp) {
if(on) {
fa->fa_fd = fd;
kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
}
}
if (on) {
new->magic = FASYNC_MAGIC;
new->fa_file = filp;
new->fa_fd = fd;
new->fa_next = *fapp;
*fapp = new;
result = 1;
}
out:
write_unlock_irq(&fasync_lock);
return result;
}
EXPORT_SYMBOL(fasync_helper);

开释信号函数
内核部分函数二)kill_fasync(struct fasync_struct * * fp, int sig, int band)
参数:sig便是咱们要发送的信号;band(带宽),一般都是运用POLL_IN,一共设备可读,假如设备可写,运用POLL_OUT
内核源码三)开释(发生)异步读信号函数

void __kill_fasync(struct fasync_struct *fa, int sig, int band)
{
while (fa) {
struct fown_struct * fown;
//假如设备文件镜像不存在如设备文件不存在(被删去或改名)或取消了注册FASYNC;镜像映射失利跳出kill_fasync,不发生信号
if (fa->magic != FASYNC_MAGIC) {
printk(KERN_ERR “kill_fasync: bad magic number in “
“fasync_struct!\n”);
return;
}
fown = &fa->fa_file->f_owner;
/* Dont send SIGURG to processes which have not set a
queued signum: SIGURG has its own default signalling
mechanism. */
if (!(sig == SIGURG && fown->signum == 0))
send_sigio(fown, fa->fa_fd, band);
fa = fa->fa_next;
}
}
EXPORT_SYMBOL(__kill_fasync);

模板二)信号的异步告知机制模板

struct VirtualDisk{
struct cdev cdev;
//…其他大局变量….
struct fasync_struct *async_queue;//异步结构体指针
};
/*异步读信号*/
static int myfasync_drv_fasync(int fd, struct file *file, int mode){
struct VirtualDisk *devp = file->private_data; /*取得设备结构体指针*/
//………………..
return fasync_helper(fd, file, mode, &devp->async_queue);
}
static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){
struct VirtualDisk *devp = file->private_data; /*取得设备结构体指针*/
//……………
//发生异步读信号SIGIO
if(devp->async_queue)kill_fasync(&devp->async_queue, SIGIO, POLL_IN);
return 0;
}
static int myfasync_drv_release(struct inode *inode, struct file *file)
{
/*当设备封闭时,需求将fasync_struct从异步行列中删去/*
myfasync_drv_fasync(-1, file, 0);
return 0;
}

实例二)驱动程序完好实例:

//“myfasync_drv”,”myfasync_”,”myfasync_drv”
#include //模块所需的很多符号和函数界说
#include #include //文件体系相关的函数和头文件
#include //指定初始化和铲除函数
#include #include //cdev结构的头文件包含#include #include //#include //包含驱动程序运用的大部分内核API的界说,包含睡觉函数以及各种变量声明
#include //在内核和用户空间中移动数据的函数
#include
#include
#include
#include
#define VIRTUALDISK_SIZE 0x1000//4k
#define MEM_CLEAR 0x1
#define VIRTUALDISK_MAJOR 250
int VirtualDisk_major = VIRTUALDISK_MAJOR;
struct fasync_struct *async_queue;//异步结构体指针
struct VirtualDisk{
struct cdev cdev;//详细看cdev机制
unsigned char mem[VIRTUALDISK_SIZE ];
long count; /*记载设备现在被多少设备翻开*/

};
static struct class *myfasync_class;
static struct class_device *myfasync_class_dev;
struct VirtualDisk *VirtualDiskp;
static int myfasync_drv_fasync(int fd, struct file *file, int mode){
printk(“myfasync_drv_fasync %d\n”, fd);
return fasync_helper(fd, file, mode, &async_queue);
}
static int myfasync_drv_open(struct inode *inode, struct file *file)
{
printk(“myfasync_drv open\n”);
file->private_data = VirtualDiskp;
VirtualDiskp->count++; /*添加设备翻开次数*/
return 0;
}
static int myfasync_drv_release(struct inode *inode, struct file *file)
{
printk(“myfasync_drv release\n”);
VirtualDiskp->count–; /*削减设备翻开次数*/
myfasync_drv_fasync(-1, file, 0);//当设备封闭时,需求将fasync_struct从异步行列中删去
return 0;
}
/*seek文件定位函数:seek()函数对文件定位的开端地址能够是文件最初(SEEK_SET,0)、当时方位(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static loff_t myfasync_drv_llseek(struct file *file, loff_t offset, int origin){
loff_t ret = 0;/*回来的方位偏移*/

switch (origin)
{
case SEEK_SET: /*相对文件开端方位偏移*/
if (offset < 0)/*offset不合法*/
{
ret = – EINVAL; /*无效的指针*/
break;
}
if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于设备内存*/
{
ret = – EINVAL; /*无效的指针*/
break;
}
file->f_pos = (unsigned int)offset; /*更新文件指针方位*/
ret = file->f_pos;/*回来的方位偏移*/
break;
case SEEK_CUR: /*相对文件当时方位偏移*/
if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于设备内存*/
{
ret = – EINVAL;/*无效的指针*/
break;
}
if ((file->f_pos + offset) < 0)/*指针不合法*/
{
ret = – EINVAL;/*无效的指针*/
break;
}
file->f_pos += offset;/*更新文件指针方位*/
ret = file->f_pos;/*回来的方位偏移*/
break;
default:
ret = – EINVAL;/*无效的指针*/
break;
}
return ret;
}
/*设备操控函数:ioctl()函数承受的MEM_CLEAR指令,这个指令将大局内存的有用数据长度清零,关于设备不支持的指令,ioctl()函数应该回来-EINVAL*/
static int myfasync_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
struct VirtualDisk *devp = file->private_data;/*取得设备结构体指针*/

switch (cmd)
{
case MEM_CLEAR:/*设备内存清零*/
memset(devp->mem, 0, VIRTUALDISK_SIZE);
printk(KERN_INFO “VirtualDisk is set to zero\n”);
break;
default:
return – EINVAL;
}
return 0;
}
/*读函数:读写函数主要是让设备结构体的mem[]数组与用户空间交互数据,并跟着拜访字节数改变回来用户的文件读写偏移方位*/
static ssize_t myfasync_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos; /*记载文件指针偏移方位*/
unsigned int countt = count;/*记载需求读取的字节数*/
int ret = 0; /*回来值*/
struct VirtualDisk *devp = file->private_data; /*取得设备结构体指针*/
printk(“myfasync_drv read\n”);
/*剖析和获取有用的读长度*/
if (p >= VIRTUALDISK_SIZE ) /*要读取的偏移大于设备的内存空间*/
return 0;/*读取地址过错*/
if (countt > VIRTUALDISK_SIZE – p)/*要读取的字节大于设备的内存空间*/
countt = VIRTUALDISK_SIZE – p;/*酿制读取的字节数设为剩下的字节数*/
/*内核空间->用户空间交流数据*/
if (copy_to_user(buf, (void*)(devp->mem + p), countt))
{
ret = – EFAULT;
}
else
{
*ppos += countt;
ret = countt;
printk(“read %d bytes(s) is %ld\n”, countt, p);
}
printk(“bytes(s) is %s\n”, devp->mem);
return ret;
}
/*
file 是文件指针,count 是恳求的传输数据长度,buff 参数是指向用户空间的缓冲区,这个缓冲区或许保存要写入的数据,或许是一个寄存新读入数据的空缓冲区,该地址在内核空间不能直接读写,ppos 是一个指针指向一个”long offset type”方针, 它指出用户正在存取的文件方位. 回来值是一个”signed size type。写的方位相关于文件最初的偏移。
*/
static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
unsigned long p = *ppos; /*记载文件指针偏移方位*/
int ret = 0; /*回来值*/
unsigned int countt = count;/*记载需求写入的字节数*/
struct VirtualDisk *devp = file->private_data; /*取得设备结构体指针*/
printk(“myfasync_drv write\n”);
/*剖析和获取有用的写长度*/
if (p >= VIRTUALDISK_SIZE )/*要写入的偏移大于设备的内存空间*/
return 0;/*写入地址过错*/
if (countt > VIRTUALDISK_SIZE – p)/*要写入的字节大于设备的内存空间*/
countt = VIRTUALDISK_SIZE – p;/*酿制写入的字节数设为剩下的字节数*/
/*用户空间->内核空间*/
if (copy_from_user(devp->mem + p, buf, countt))
ret = – EFAULT;
else
{
*ppos += countt;/*添加偏移方位*/
ret = countt;/*回来实践的写入字节数*/
printk(“written %u bytes(s) from%lu, buffer is %s\n”, countt, p, devp->mem);
}
if(async_queue){
kill_fasync(&async_queue, SIGIO, POLL_IN);
printk(“write kill_fasync\n”);
}
return ret;
}
static struct file_operations myfasync_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,面向编译模块时主动创立的__this_module变量 */
.open = myfasync_drv_open,
.read = myfasync_drv_read,
.write = myfasync_drv_write,
.release = myfasync_drv_release,
.llseek = myfasync_drv_llseek,
.ioctl = myfasync_drv_ioctl,
.fasync = myfasync_drv_fasync,
};
/*将 cdev 结构嵌入一个你自己的设备特定的结构,你应当初始化你现已分配的结构运用以上函数,有一个其他的 struct cdev 成员你需求初始化. 象 file_operations 结构,struct cdev 有一个具有者成员,应当设置为 THIS_MODULE,一旦 cdev 结构树立, 最终的过程是把它告知内核, 调用:
cdev_add(&dev->cdev, devno, 1);*/
static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minorIndex){
int err;
int devno = MKDEV(VirtualDisk_major, minorIndex);
cdev_init(&dev->cdev, &myfasync_drv_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);
if(err){
printk(“error %d cdev file added\n”, err);
}
}
static int myfasync_drv_init(void)
{
int result;
dev_t devno = MKDEV(VirtualDisk_major, 0);
if(VirtualDisk_major){
result = register_chrdev_region(devno, 1, “myfasync_drv”);
}else{
result = alloc_chrdev_region(&devno, 0, 1, “myfasync_drv”);
VirtualDisk_major = MAJOR(devno);
}
if(result < 0 ){
return result;
}
VirtualDiskp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
if(!VirtualDiskp){
result = -ENOMEM;
goto fail_malloc;
}
memset(VirtualDiskp, 0, sizeof(struct VirtualDisk));
VirtualDisk_setup_cdev(VirtualDiskp, 0);
myfasync_class = class_create(THIS_MODULE, “myfasync_drv”);
if (IS_ERR(myfasync_class))
return PTR_ERR(myfasync_class);
myfasync_class_dev = class_device_create(myfasync_class, NULL, MKDEV(VirtualDisk_major, 0), NULL, “myfasync_drv”); /* /dev/xyz */
if (IS_ERR(myfasync_class_dev))
return PTR_ERR(myfasync_class_dev);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;

}
static void myfasync_drv_exit(void)
{
cdev_del(&VirtualDiskp->cdev);
kfree(VirtualDiskp);
unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
class_device_unregister(myfasync_class_dev);
class_destroy(myfasync_class);
}
module_init(myfasync_drv_init);
module_exit(myfasync_drv_exit);
MODULE_L%&&&&&%ENSE(“GPL”);

Makefile

#myfasync_drv.c
KERN_DIR = /workspacearm/linux-2.6.2.6
all:
make -C $(KERN_DIR) M=`pwd` modules
cp myfasync_drv.ko /opt/fsmini/
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf timerlists.order
obj-m += myfasync_drv.o

实例三)驱动程序对应的测验的应用程序部分

#include
#include
#include
#include
#include
int myfd;
int lenthe;
void input_handler(int num)
{
char data[80];
int len;
lseek(myfd, -lenthe, SEEK_CUR);//移动偏移量到写之前方位
len = read(myfd, data, lenthe);
//data[len] = ;
printf(“myfd = %d, len = %d buffuer input available :%s\n”,myfd, len, data);
}
void setFdAsync(int fd){
int oflags;
//当时进程变成文件的主人
fcntl(fd, F_SETOWN, getpid());
//本程序中fd = STDIN_FILENO规范输入设备设备文件描绘符号;一般文件内核中没有完结FASYNC,不能运用异步通讯
oflags = fcntl(fd, F_GETFL);//
//FASYNC在glibc 的fcntl.h文件中能够看到这样的界说 #define FASYNC O_ASYNC
fcntl(fd, F_SETFL, oflags | FASYNC);
}
int main(){
myfd = open(“/dev/myfasync_drv”, O_RDWR);//STDIN_FILENO输入输出设备描绘符号,一般是键盘
printf(“fd = %d,pid = %d”, myfd, getpid());
signal(SIGIO,input_handler);//设置好方针设备的SIGIO信号处理程序;等候内核kill_fasync()开释 SIGIO 信号
setFdAsync(myfd);
printf(“before while\n”);
while(1){
char buffer[80];
lenthe = read(STDIN_FILENO, buffer, 80);
write(myfd, buffer, lenthe);
}
return 0;
}

我的Makefile

objs := $(patsubst %c, %o, $(shell ls *.c))
myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc
mybutton.bin:$(objs)
$(myarmgcc) -o $@ $^
cp *.bin /opt/fsmini/
%.o:%.c
$(myarmgcc) -c -o $@ $<
clean:
rm -f *.bin *.o

试验成果

# insmod myfasync_drv.ko
# ./mybutton.bin
myfasync_drv open//对应应用程序myfd = open(“/dev/myfasync_drv”,调用了内核驱动open函数
myfasync_drv_fasync 3//对应应用程序fcntl(fd, F_SETFL, oflags | FASYNC);调用了内核驱动的myfasync_drv_fasync()函数
//
fd = 3,pid = 793before while//while前的进程信息输出
hello//键盘输入hello
myfasync_drv write//调用驱动程序write函数
written 6 bytes(s) from0, buffer is hello//驱动程序write函数内部输出
write kill_fasync//内在write函数中,碑文kill_fasync(&async_queue, SIGIO, POLL_IN);开释SIGIO信号
myfasync_drv read//此刻应用程序收到中止,应用程序碑文read函数,read对应内核驱动的read
read 6 bytes(s) is 0//内核驱动read打印输出
bytes(s) is hello //内核驱动read打印输出

myfd = 3, len = 6 buffuer input available :hello//应用程序input_handler函数输出驱动的写入值
//下面是while第2次碑文
it is ok
myfasync_drv write
written 9 bytes(s) from6, buffer is hello
it is ok

write kill_fasync
myfasync_drv read
read 9 bytes(s) is 6
bytes(s) is hello
it is ok

myfd = 3, len = 9 buffuer input available :it is ok
//按ctrl+c退出程序,会碑文myfasync_drv_release中myfasync_drv_fasync(-1, file, 0),开释本进程的异步告知
myfasync_drv release
myfasync_drv_fasync -1

#

四、异步IO缺点:当有多个文件发送异步告知信号给一个进程时,进程无法知道是哪个文件发送的信号,这时候“设备文件 ”仍是要凭借poll机制完结IO;(应用程序中运用select)

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qianrushi/xitong/265203.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部