前面字符设备驱动读写时《字符设备驱动(3)-read、write》,我们用户态程序是先往设备写数据,然后再读出来,数据是有效的,但是如果先读数据,这时设备里面没有数据,那么对读来说就是资源不可用,同理,如果设备的缓存(内核中的缓存)如果满了,我们往设备写数据,写也不会成功。当资源不可用时,用户态程序和驱动一起的各种配合就组成了多种I/O模型。这些I/O模型分为非阻塞I/O,阻塞I/O,多路复用,信号驱动和异步I/O。
基本概念
为了理解这几种模型,我们来举个等吃饭的例子:
非阻塞I/O-----一会儿就进厨房,看饭做好没有。
阻塞I/O--------在厨房,盯着做饭。
多路复用------厨房有100个灶台同时做饭,有个好了,就可以吃了。
信号驱动------厨房里,饭做好了,通知你吃饭。
异步I/O------把在厨房盯着做饭的活交给李四,做好后,李四再通知你。
在这个故事中,厨房是操作系统,饭是数据,做好饭是数据就绪条件。
那非阻塞I/O就是用户态程序轮询过程,当资源不可用时,驱动就立即返回,并用一个错误码EAGAIN来通知资源不可用,然后用户态程序稍后再尝试。
用户态程序
app.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#define FILE_NAME "/dev/mydev"
#define MAX_LEN 64
int fd;
void *func(void *arg)
{
char buf[MAX_LEN] = {0};
int rlen;
while (1)
{
rlen = read(fd, buf, MAX_LEN - 1);
if (rlen > 0)
{
printf("Read buf %s len %d success.\n", buf, rlen);
return NULL;
}
else if (errno == EAGAIN)
{
sleep(1);
printf("Device is not ready please wait.\n");
}
else
{
perror("Read error");
return NULL;
}
}
}
int main(void)
{
int wlen;
char buf[MAX_LEN] = "abcdefg";
pthread_t td;
//注意以非阻塞方式打开文件
fd = open(FILE_NAME, O_RDWR|O_NONBLOCK);
if (0 > fd)
{
printf("Open failed.\n");
return -1;
}
pthread_create(&td, NULL, func, NULL);
sleep(5);
wlen = write(fd, buf, strlen(buf));
if (wlen < 0)
{
perror("Write error");
return wlen;
}
printf("Write buf len %d success.\n", wlen);
while(1)
{
}
return 0;
}
注意编译时,需要加上库pthread
$gcc app.c -l pthread
驱动程序关键点
static char kbuf[MAX_LEN] = {0};
static int kbuf_len = 0;
ssize_t my_read(struct file *pf, char __user *ubuf, size_t len, loff_t *pl)
{
int ret = -1;
if (MAX_LEN < len)
{
printk("len is large than %d.\n", MAX_LEN);
return -1;
}
//判断设备缓存是否为空
if (0 == kbuf_len)
{
if (pf->f_flags& O_NONBLOCK)
return -EAGAIN;
}
ret = copy_to_user(ubuf, kbuf, len);
if (0 != ret)
{
printk("Copy to user failed.\n");
return -1;
}
//读出后,把对应的设备数据清空,保留未读出数据
memcpy(kbuf, kbuf + len, MAX_LEN - len);
memset(kbuf + MAX_LEN - len, 0, len);
//计算设备缓存长度
if (kbuf_len > len)
kbuf_len -= len;
else
kbuf_len = 0;
return len;
}
ssize_t my_write(struct file *pf, const char __user *ubuf, size_t len, loff_t *pl)
{
int ret = -1;
//判断是否会写超过
if (MAX_LEN < len + kbuf_len)
{
printk("len is large than %d.\n", MAX_LEN);
return -1;
}
if (MAX_LEN == kbuf_len)
{
if (pf->f_flags& O_NONBLOCK)
return -EAGAIN;
}
ret = copy_from_user(kbuf + kbuf_len, ubuf, len);
if (0 != ret)
{
printk("Copy from user failed.\n");
return -1;
}
kbuf_len += len;
return len;
}
完整驱动程序
//head
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/string.h>
#define MAJOR_CHAR 100
#define MINOR_CHAR 0
#define MAX_LEN 64
static int my_open(struct inode *pnode, struct file *pfile)
{
printk("Open cdev.\n");
return 0;
}
static int my_close(struct inode *pnode, struct file *pfile)
{
printk("Close cdev.\n");
return 0;
}
static char kbuf[MAX_LEN] = {0};
static int kbuf_len = 0;
ssize_t my_read(struct file *pf, char __user *ubuf, size_t len, loff_t *pl)
{
int ret = -1;
if (MAX_LEN < len)
{
printk("len is large than %d.\n", MAX_LEN);
return -1;
}
//判断设备缓存是否为空
if (0 == kbuf_len)
{
if (pf->f_flags& O_NONBLOCK)
return -EAGAIN;
}
ret = copy_to_user(ubuf, kbuf, len);
if (0 != ret)
{
printk("Copy to user failed.\n");
return -1;
}
//读出后,把对应的设备数据清空,保留未读出数据
memcpy(kbuf, kbuf + len, MAX_LEN - len);
memset(kbuf + MAX_LEN - len, 0, len);
//计算设备缓存长度
if (kbuf_len > len)
kbuf_len -= len;
else
kbuf_len = 0;
return len;
}
ssize_t my_write(struct file *pf, const char __user *ubuf, size_t len, loff_t *pl)
{
int ret = -1;
//判断是否会写超过
if (MAX_LEN < len + kbuf_len)
{
printk("len is large than %d.\n", MAX_LEN);
return -1;
}
if (MAX_LEN == kbuf_len)
{
if (pf->f_flags& O_NONBLOCK)
return -EAGAIN;
}
ret = copy_from_user(kbuf + kbuf_len, ubuf, len);
if (0 != ret)
{
printk("Copy from user failed.\n");
return -1;
}
kbuf_len += len;
return len;
}
struct cdev cdevice;
struct file_operations cdev_ops = {
.open = my_open,
.release = my_close,
.read = my_read,
.write = my_write,
};
//加载
static int hello_init(void)
{
dev_t devno = MKDEV(MAJOR_CHAR,MINOR_CHAR);
int ret = -1;
printk(KERN_ALERT "Hello World.\n");
//up kernel
//1、注册设备号
ret = register_chrdev_region(devno, 1, "hello");
if (0 != ret)
{
printk("Register char device failed.\n");
return ret;
}
//2、初始化字符设备结构体
cdev_init(&cdevice, &cdev_ops);
cdevice.owner = THIS_MODULE;
//3、添加字符设备结构体给内核
ret = cdev_add(&cdevice,devno , 1);
if (0 != ret)
{
//注意释放设备号
unregister_chrdev_region(devno,1);
printk("Unregister char device.\n");
return ret;
}
printk("Register char device success.\n");
//down hardware
return 0;
}
//卸载函数(必须)
static void hello_exit(void)//返回值是void类型,函数名自定义,参数是void
{
dev_t devno = MKDEV(MAJOR_CHAR, MINOR_CHAR);
printk(KERN_ALERT "Goodbye World.\n");
// down hardware
// up kernel
//1、从内核中删除字符设备结构体
cdev_del(&cdevice);
//2、注销设备号
unregister_chrdev_region(devno, 1);
}
//注册(必须)
module_init(hello_init);
module_exit(hello_exit);
//license(必须)
MODULE_LICENSE("GPL");
//作者与描述(可选)
MODULE_AUTHOR("Ono Zhang");
MODULE_DESCRIPTION("A simple Hello World Module");
运行结果:
$ sudo mknod /dev/mydev c 100 0
$ sudo insmod cdev.ko
$ sudo ./a.out
Device is not ready please wait.
Device is not ready please wait.
Device is not ready please wait.
Device is not ready please wait.
Write buf len 7 success.
Device is not ready please wait.
Read buf abcdefg len 63 success.
tobecontinue
每周三、周六更新