SylixOS设备操作之非阻塞read和write
上一节学习了阻塞方式对驱动进行读写,但是在某些情况下,我们需要非阻塞地读写驱动,如果数据没准备好,read或write调用需要立即返回以进行后续的业务处理。在SylixOS应用层,可以通过三种方法来进行非阻塞read或write调用。在本小节教程中,我们同样以read为例讲解非阻塞。
1. 以非阻塞模式打开设备文件时
应用层在以open 接口打开设备文件时,打开标志中添加O_NONBLOCK 即可以非阻塞的方式打开设备:
fd = open("/dev/demo0", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("open file fail.\r\n"); return -1; }
驱动层也要做相应的修改,以对非阻塞调用进行处理,首先demo_dev_t 数据结构中需要添加一个成员用于记录当前是否需要进行非阻塞处理:
typedef struct _demo_dev { LW_DEV_HDR dev; int flag; void *priv; } demo_dev_t;
驱动open 函数中需要对打开标志进行判断,如果带有非阻塞标志,则需要做相应的处理:
if (flag & O_NONBLOCK) { printk("open nonblock.\r\n"); device->flag |= O_NONBLOCK; }
驱动中的read 也要判断当前设备的阻塞类型来决定到底如何调用虚拟外设的读数据接口:
static ssize_t demo_read(demo_dev_t *dev, char *buf, size_t size) { int ret = -1; BOOL non_block = FALSE; printk("%s.\r\n", __func__); if (dev->flag & O_NONBLOCK) { non_block = TRUE; } else { non_block = FALSE; } ret = vir_device_read(non_block); if (ret > 0) memcpy(buf, &ret, sizeof(int)); return ret; }
驱动中如果没有读取到数据,接口是返回0的,所以在应用层我们需要对read数据为0分支做小改动:
ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { sleep(1); printf("read data end.\r\n"); } else { printf("read %d data,data is: %d.\r\n", ret, data); }
与之前的处理代码相比,多了个sleep(1)处理,因为如果不加这条语句,终端上会一直打印read data end. 从而影响我们查看别的打印信息。加载驱动并运行程序后,终端上的打印:
[root@sylixos:/apps/app_demo11_2]# ./app_demo11_2 demo_open . open nonblock. open read/write. file permission: 644. demo_read. read 10 data,data is: 10. demo_read. read data end. demo_read. read data end. demo_read. read 11 data,data is: 11. demo_read. read data end. demo_read. read data end. demo_read. read data end. demo_read. read 12 data,data is: 12. demo_read.
可以看到程序确实走的是read为0的分支。
2. 使用ioctl设置设备为非阻塞模式
这种方式打开设备时还是以默认的阻塞方式打开,打开成功后,通过调用FIONBIO ioctl命令来设置设备文件为非阻塞模式:
int non_block = 0; non_block = 1; ret = ioctl(fd, FIONBIO, &non_block); if (ret) { printf("FIONBIO fail.\r\n"); close(fd); return -1; }
FIONBIO 命令的参数为一个int类型变量的地址,变量值为1表示需要设置为非阻塞模式,为0表示取消非阻塞模式。驱动层的ioctl函数中需要对FIONBIO 命令做相应的处理:
case FIONBIO: if (*(int *)arg) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break;
驱动层和应用层read函数还是和第一种方法一样,不需要修改,程序运行输出也一样,这里就不贴出来了。
3. 使用fcntl设置设备为非阻塞模式
这种方式有点类似前两种方法的结合,首先通过F_GETFL 命令获取设备文件当前的打开标志,然后加上O_NONBLOCK 作为新的文件标志通过F_SETFL 命令再设置到设备文件中:
ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); if (ret) { printf("F_SETFL fail.\r\n"); close(fd); return -1; }
驱动层的ioctl函数中需要对F_SETFL 命令做相应的处理,如下所示:
case FIOSETFL: if ((int)arg & O_NONBLOCK) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break;
驱动层和应用层read函数还是和前两种方法一样,不需要修改,程序运行输出也一样,这里也不贴出来了。
通过前面三种方法我们可以看出最终的目的都是改变驱动中的标志来告知驱动read或write函数使用何种方式进行操作,所以应用开发者想要使用阻塞或者非阻塞方式来操作设备,其实是需要驱动来提供相应的功能的。
附源码
driver_demo11.c源码:
#define __SYLIXOS_KERNEL #include <SylixOS.h> #include <module.h> #include <string.h> #include "driver.h" #include "vir_peripheral_device.h" typedef struct _demo_dev { LW_DEV_HDR dev; int flag; void *priv; } demo_dev_t; int drv_index; demo_dev_t demo_dev[2]; static long demo_open(LW_DEV_HDR *dev, char *name, int flag, int mode) { demo_dev_t *device = (demo_dev_t *)dev; device->priv = NULL; device->flag = 0; printk("%s %s.\r\n", __func__, name); if (flag & O_NONBLOCK) { printk("open nonblock.\r\n"); device->flag |= O_NONBLOCK; } if ((flag & O_ACCMODE) == O_RDONLY) { printk("open read only.\r\n"); } else if ((flag & O_ACCMODE) == O_WRONLY) { printk("open write only.\r\n"); } else { printk("open read/write.\r\n"); } printk("file permission: %o.\r\n", mode); LW_DEV_INC_USE_COUNT(&device->dev); return (long)device; } static int demo_close(demo_dev_t *dev) { printk("%s.\r\n", __func__); LW_DEV_DEC_USE_COUNT(&dev->dev); return ERROR_NONE; } static int demo_ioctl(demo_dev_t *dev, int cmd, long arg) { struct stat *pstat; struct data *data; printk("%s %x.\r\n", __func__, cmd); switch (cmd) { case FIOFSTATGET: pstat = (struct stat *)arg; if (!pstat) { return (PX_ERROR); } pstat->st_dev = LW_DEV_MAKE_STDEV(&dev->dev); pstat->st_ino = (ino_t)0; pstat->st_mode = (S_IRWXU | S_IRWXG | S_IRWXO | S_IFCHR); pstat->st_nlink = 1; pstat->st_uid = 0; pstat->st_gid = 0; pstat->st_rdev = 0; pstat->st_size = 0; pstat->st_blksize = 0; pstat->st_blocks = 0; pstat->st_atime = API_RootFsTime(LW_NULL); pstat->st_mtime = API_RootFsTime(LW_NULL); pstat->st_ctime = API_RootFsTime(LW_NULL); break; case FIONBIO: if (*(int *)arg) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break; case FIOSETFL: if ((int)arg & O_NONBLOCK) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break; case CMD_GET_VERSION: data = (struct data *)arg; data->version = 3; break; default: return (PX_ERROR); } return ERROR_NONE; } static ssize_t demo_read(demo_dev_t *dev, char *buf, size_t size) { int ret = -1; BOOL non_block = FALSE; printk("%s.\r\n", __func__); if (dev->flag & O_NONBLOCK) { non_block = TRUE; } else { non_block = FALSE; } ret = vir_device_read(non_block); if (ret > 0) memcpy(buf, &ret, sizeof(int)); return ret; } static ssize_t demo_write(demo_dev_t *dev, char *buf, size_t size) { char wdata[30] = {0}; int wdata_len = 30; printk("%s.\r\n", __func__); wdata_len = (size < wdata_len) ? size : wdata_len; memcpy(wdata, buf, size); printk("write data %s success.\r\n", wdata); return wdata_len; } static struct file_operations demo_fops = { .owner = THIS_MODULE, .fo_open = demo_open, .fo_close = demo_close, .fo_ioctl = demo_ioctl, .fo_read = demo_read, .fo_write = demo_write, }; int module_init (void) { int ret = 0; drv_index = iosDrvInstallEx(&demo_fops); if (drv_index < 0) { printk("driver install fail.\r\n"); return -1; } DRIVER_LICENSE(drv_index, "GPL->Ver 2.0"); DRIVER_AUTHOR(drv_index, "GeWenBin"); DRIVER_DESCRIPTION(drv_index, "demo driver."); ret = iosDevAdd(&demo_dev[0].dev, "/dev/demo0", drv_index); if (ret != ERROR_NONE) { printk("device add fail.\r\n"); iosDrvRemove(drv_index, TRUE); return -1; } ret = iosDevAdd(&demo_dev[1].dev, "/dev/demo1", drv_index); if (ret != ERROR_NONE) { printk("device1 add fail.\r\n"); iosDevDelete(&demo_dev[0].dev); iosDrvRemove(drv_index, TRUE); return -1; } vir_device_init(); return 0; } void module_exit (void) { iosDevDelete(&demo_dev[0].dev); iosDevDelete(&demo_dev[1].dev); iosDrvRemove(drv_index, TRUE); }
方法1应用层源码:
#include <stdio.h> #include <string.h> int main (int argc, char **argv) { int fd; int ret = 0; int data = -1; fd = open("/dev/demo0", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("open file fail.\r\n"); return -1; } while (1) { ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { sleep(1); printf("read data end.\r\n"); } else { printf("read %d data,data is: %d.\r\n", ret, data); } } close(fd); return (0); }
方法2应用层源码:
#include <stdio.h> #include <string.h> int main (int argc, char **argv) { int fd; int ret = 0; int data = -1; int non_block = 0; fd = open("/dev/demo0", O_RDWR); if (fd < 0) { printf("open file fail.\r\n"); return -1; } non_block = 1; ret = ioctl(fd, FIONBIO, &non_block); if (ret) { printf("FIONBIO fail.\r\n"); close(fd); return -1; } while (1) { ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { sleep(1); printf("read data end.\r\n"); } else { printf("read %d data,data is: %d.\r\n", ret, data); } } close(fd); return (0); }
方法3应用层源码:
#include <stdio.h> #include <string.h> int main (int argc, char **argv) { int fd; int ret = 0; int data = -1; fd = open("/dev/demo0", O_RDWR); if (fd < 0) { printf("open file fail.\r\n"); return -1; } ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); if (ret) { printf("F_SETFL fail.\r\n"); close(fd); return -1; } while (1) { ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { sleep(1); printf("read data end.\r\n"); } else { printf("read %d data,data is: %d.\r\n", ret, data); } } close(fd); return (0); }
2021年9月17日 14:02 1F
打卡。