设备不一定随时能给客户提供服务,有资源可用和不可用两种状态,对于虚拟串口来说,用户想要读取数据,如果FIFO的数据是空的,那么就无法正常读取数据,但此时可以正常写入,反之,如果此时FIFO数据是满的,那么就能正常读取数据,但无法正常写入数据。当资源不可用时,应用程序和驱动一起的各种配合可以组成多种I/O模型。如果程序已非阻塞的方式打开文件时,当资源不可用时,驱动就会立刻返回,并返回一个错误码-EAGIN来通知应用程序此时资源不可用。完整代码如下
驱动代码:
/*************************************************************************
> File Name: unblockio.c
> Author: longway.bai
> Mail: 953821672@qq.com
> Created Time: 2021年12月12日 星期日 16时22分50秒
************************************************************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>
#define CHRDEV_MAJOR 256
#define CHRDEV_MINOR 0
#define CHRDEV_CNT 1
#define CHRDEV_NAME "chrdev"
static struct cdev vsdev;
DEFINE_KFIFO(vsfifo, char, 32);
static int chrdev_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int chrdev_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t chrdev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
int ret;
unsigned int copied;
if((kfifo_is_empty(&vsfifo)) && (filp->f_flags & O_NONBLOCK)) //用户层已非阻塞的方式读,且kfifo已经empty的话则返回error
return -EAGAIN;
ret = kfifo_to_user(&vsfifo, buf, count, &copied);
return ret == 0 ? copied : ret;
}
static ssize_t chrdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
int ret;
unsigned int copied;
if((kfifo_is_full(&vsfifo)) && (filp->f_flags & O_NONBLOCK)) //用户层已非阻塞的方式写,且kfifo已经full的话则返回error
return -EAGAIN;
ret = kfifo_from_user(&vsfifo, buf, count, &copied);
return ret == 0 ? copied : ret;
}
static struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open,
.release = chrdev_release,
.read = chrdev_read,
.write = chrdev_write,
};
static int __init chrdev_init(void)
{
int ret;
dev_t dev;
dev = MKDEV(CHRDEV_MAJOR, CHRDEV_MINOR);
ret = register_chrdev_region(dev, CHRDEV_CNT, CHRDEV_NAME);
if (ret)
goto reg_err;
cdev_init(&vsdev, &chrdev_ops);
vsdev.owner = THIS_MODULE;
ret = cdev_add(&vsdev, dev, CHRDEV_CNT);
if (ret)
goto add_err;
return 0;
add_err:
unregister_chrdev_region(dev, CHRDEV_CNT);
reg_err:
return ret;
}
static void __exit chrdev_exit(void)
{
dev_t dev;
dev = MKDEV(CHRDEV_MAJOR, CHRDEV_MINOR);
cdev_del(&vsdev);
unregister_chrdev_region(dev, CHRDEV_CNT);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("longway<longway.bai@outlook.com>");
MODULE_DESCRIPTION("A simple module");
MODULE_ALIAS("virtual-serial");
应用层代码做测试
/*************************************************************************
> File Name: test.c
> Author: longway.bai
> Mail: 953821672@qq.com
> Created Time: 2021年12月12日 星期日 17时02分48秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define DEV_NAME "/dev/vser" //这里定义设备节点
#define TEST_LEN 32
int main(int argc, char *argv[])
{
int ret;
int fd = -1;
char wbuf[TEST_LEN]="This is my i/o test dmeo";
char rbuf[TEST_LEN]={};
fd = open(DEV_NAME, O_RDWR | O_NONBLOCK);
if(fd < 0)
goto fail;
ret = read(fd, rbuf, TEST_LEN); //open后先做读的操作,此时fifo应该为空,查看此时状态
if(ret < 0)
perror("first read error");
printf("test char %s, len %ld\n", rbuf, strlen(rbuf));
ret = write(fd, wbuf, TEST_LEN); //第一次写操作
if(ret != TEST_LEN)
perror("first write error");
ret = write(fd, wbuf, TEST_LEN); //第二次写操作,如果已经超过32的字节的大小会是什么样的情况?
if(ret != TEST_LEN)
perror("second write error");
ret = read(fd, rbuf, TEST_LEN); //第二次读,之前已经正常写入后,这次读操作是什么样的?
if(ret < 0)
perror("after write,read error");
printf("test char %s, len %ld\n", rbuf, strlen(rbuf));
close(fd);
exit(EXIT_SUCCESS);
fail:
perror("advio test");
exit(EXIT_FAILURE);
}
测试结果
# mknod /dev/vser c 256 0
# ./test
first read error: Resource temporarily unavailable
test char , len 0
second write error: Resource temporarily unavailable
test char This is my i/o test dmeo, len 24