01_IO模型的类型
在linux内核字符驱动的框架理解输入输出的读写。字符设备文件操作集file_operations可以重写read、write、epoll。类型涉及:阻塞、非阻塞、异步通知、IO多路复用。
02_举例描述
02_01非阻塞与阻塞
例如函数read(fd,buf,sizeof(buf)),是把fd文件读取到buf中,实现了把字符设备文件的东西读到用户程序自己创建的缓冲数组,从而打印查看。在内核重写的.read=myread中,用copy_to_user(ubuf,kbuf,sizeof(kuf)),把内核缓冲区的值传给用户缓冲区。由于copy_to_user的本质是memcpy函数,即一个字符一个字符的拷贝。那么这个read函数的实现是非阻塞的:拷贝失败,则为空。
void* My_memcpy(void* dest,const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
如果要实现read的阻塞效果,就需要用到工作队列:
//定义等待队列
wait_queue_head_t my_queue;
//初始化等待队列
init_waitqueue_head(&my_queue);
//等待事件
wait_event_interruptible(queue,conditon)
//唤醒队列
wake_up_interruptible(wait_queue_head_t *queue);
应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
static int fd = -1;
static char wbuf[16]="1234567890";
static char rbuf[16]={0};
void* fun0(void* arg)
{
sleep(3);
printf("write ok");
write(fd,wbuf,16);
}
int main()
{
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("fd is %d\n",fd);
return -1;
}
pthread_t t0;
pthread_create(&t0,NULL,fun0,NULL);
pthread_detach(t0);
char rbuf[16]={0};
read(fd,rbuf,16);
printf("rbuf is %s\n",rbuf);
close(fd);
return 0;
}
驱动:
//头文件
#include <linux/init.h>
#include <linux/module.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/delay.h>
#include<linux/sched.h>
#define MA 300
#define MI 0
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mydev;
static char kbuf[16]={0};
static int ret = -1;
static int flag = 0;
static wait_queue_head_t my_queue;
int myopen(struct inode *pi,struct file *pf)
{
printk("myopen success\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
printk("myrelease success\n");
return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
while(flag==0)
{
msleep(1);
}
wait_event_interruptible(my_queue,flag>0)
ret = copy_to_user(ubuf,kbuf,len);
if(ret!=0)
return -1;
flag = 0;
return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
ret = copy_from_user(kbuf,ubuf,len);
if(ret!=0)
return -1;
flag = len;
wake_up_interruptible(&my_queue);
return len;
}
static const struct file_operations fops={
.open = myopen,
.release = myrelease,
.read = myread,
.open = myopen,
};
//函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
init_waitqueue_head(&my_queue);
printk("init ok\n");
return 0;
}
static void myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("exit ok\n");
return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");
02_02异步通知
阻塞和非阻塞是主动获取资源,异步通知是被动获取资源,通过收到信号,执行相应的信号处理。
异步通知的步骤:
1.绑定信号和回调函数.
2.将进程号和设备绑定,添加到内核f_owner。(每个进程的用户空间都能操作设备文件,在内核空间的f_owner是共享的,只有一个)
3.fcntl将fd 的设备,FASYNC参数(像ioctl的arg),初始化 异步通知队列
4.信号进入异步通知队列,队列发送到信号处理函数,执行回调函数
异步通知框架:
//异步结构定义 struct fasync_struct *async_queue; //异步初始化 static int myfasync(int fd,struct file *filp,int mode) { return fasync_helper(fd,filp,mode,&async_queue); } //发送信号 kill_fasync(&async_queue,SIGIO,POLL_IN);
用应用程序发送信号和驱动实现异步通知
应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "cmd.h"
static char wbuf[16]="abcdef";
static char rbuf[16]={0};
static int fd = -1;
void handler(int no)
{
read(fd,rbuf,16);
printf("rubf is %s\n",rbuf);
}
int main(int argc, char *argv[])
{
int oflags = 0;
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("fd is %d\n",fd);
return -1;
}
signal(SIGIO, handler);
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
sleep(3);
write(fd,wbuf,16);
while(1);
close(fd);
return 0;
}
驱动:
//头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/signal.h>
#include "cmd.h"
#include <asm-generic/siginfo.h>
#define MA 300
#define MI 0
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mydev;
static char kbuf[16]={0};
static int ret = -1;
static wait_queue_head_t my_queue;
static struct fasync_struct *async_queue;
static int myfasync(int fd,struct file *filp ,int mode)
{
return fasync_helper(fd,filp,mode,&async_queue);
}
int myopen(struct inode *pi,struct file *pf)
{
printk("myopen success\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
printk("myrelease success\n");
return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
ret = copy_to_user(ubuf,kbuf,len);
if(ret!=0)
return -1;
return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
ret = copy_from_user(kbuf,ubuf,len);
if(ret!=0)
return -1;
kill_fasync(&async_queue,SIGIO,POLL_IN);
return len;
}
static const struct file_operations fops={
.open = myopen,
.release = myrelease,
.read = myread,
.write = mywrite,
.fasync = myfasync,
};
//函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
init_waitqueue_head(&my_queue);
printk("init ok\n");
return 0;
}
static void myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("exit ok\n");
return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");
02_03IO多路复用
用户程序的select,poll,epoll在内核中都是调用poll函数。poll函数用到了等待队列,POLLIN是读,POLLOUT是写。
用户程序:
#include <sys/select.h>
int main()
{
int fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
perror("fd is %d\n",fd);
return -1;
}
fd_set = rfds;
fd_set = wfds;
while(1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd,&rfds);
FD_SET(fd,&wfds);
select(fd+1, &rfds, &wffs, NULL, 0);
if(FD_ISSET(fd,&rfds))
{
read(fd,rbuf,16);
printf("rbuf is %s\n",buf);
}
if(FD_ISSET(fd,&wfds))
{
write(fd,wbuf,16);
printf("write ok\n");
}
}
}
驱动
//头文件
#include <linux/init.h>
#include <linux/module.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mydev;
static char kbuf[16]={0};
static int ret = -1;
static wait_queue_head_t my_queue;
static int flag = 0;
int myopen(struct inode *pi,struct file *pf)
{
printk("myopen success\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
printk("myrelease success\n");
return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
spin_lock(&lock);
ret = copy_to_user(ubuf,kbuf,len);
spin_unlock(&lock);
if(ret!=0)
return -1;
flag = 1;/
return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
spin_lock(&lock);
ret = copy_from_user(kbuf,ubuf,len);
spin_unlock(&lock);
if(ret!=0)
return -1;
return len;
}
unsigned int mypoll(struct file *pf, struct poll_table_struct *pt)
{
unsigned int mask = 0;
poll_wait (pf,&my_queue,pt);
if(flag==1)
{
mask |= POLLIN | POLLRDNORM;
}
if(flag==0)
{
mask |= POLLOUT | POLLRDNORM;
}
return mask;
}
static const struct file_operations fops={
.open = myopen,
.release = myrelease,
.read = myread,
.open = myopen,
.poll = mypoll
};
//函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
init_waitqueue_head(&my_queue);
printk("init ok\n");
return 0;
}
static void myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("exit ok\n");
return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");