SylixOS设备操作之阻塞read和write
在实际的外设驱动中,如果需要读取设备数据,设备都需要一定的准备时间,比如读硬盘扇区数据,硬盘需要一段时间准备好数据后,通过中断来通知cpu数据准备好。在数据没准备好之前,驱动主要有三种处理策略:
- 轮询方法:通过一直查询相关寄存器直到确认数据准备好。这种方式编程简单,但是由于是轮询方式,cpu占用率高,降低程序并行执行效率。
- 睡眠线程等待通知:将线程设置为阻塞状态,并在中断处理程序中唤醒阻塞的线程。这种方式编程略微复杂,因为要设置中断处理程序,但是好处是提高了cpu并行执行程序的能力。
- 不处理直接返回:这种方式不做任何处理,直接从驱动返回,这时应用层的代码就需要严格判断read返回值来确定到底有没有读取到数据。
一般驱动read和write操作默认都是阻塞的,如何进行非阻塞read和write我们将在下一节教程中介绍。本小节以read为例讲解阻塞操作,write操作类似不作讲解。另外本节我们在驱动里编写了一个虚拟的外设,同时我们为封装了主要的两个接口来操作这个外设,一个是初始化接口:
int vir_device_init(void);
成功返回ERROR_NONE,失败返回PX_ERROR,另外还提供了读取外设数据的接口:
int vir_device_read (BOOL non_block);
non_block 参数表示是否需要非阻塞读取数据,此虚拟外设每隔3秒钟产生一个新的数据(其实就是将一个变量从1开始每隔3S加一),如果读取的时候数据没有准备好,会根据传入的参数来决定是阻塞等待数据还是直接返回。函数正常返回一个正数数值,失败返回PX_ERROR,如果非阻塞模式没有读取到数据则返回0。
将上一节教程中的read函数进行修改:
static ssize_t demo_read(demo_dev_t *dev, char *buf, size_t size) { int data = -1; printk("%s.\r\n", __func__); data = vir_device_read(FALSE); memcpy(buf, &data, sizeof(int)); return sizeof(int); }
我们本节学习的是阻塞读取数据,所以vir_device_read 参数传入的是FALSE,应用层读取数据的流程:
while (1) { ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { printf("read data end.\r\n"); } else { printf("read %d data,data is: %d.\r\n", ret, data); } }
正常执行的结果就是每隔3S终端上打印读取到的数据:
[root@sylixos:/apps/app_demo10]# ./app_demo10 demo_open . open read/write. file permission: 644. demo_read. read 4 data,data is: 2. demo_read. read 4 data,data is: 3. demo_read. read 4 data,data is: 4. demo_read. read 4 data,data is: 5. demo_read. read 4 data,data is: 6. demo_read.
如上所示,每次从驱动中读取的数据都是自增的。
附源码
driver_demo10.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; 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; printk("%s %s.\r\n", __func__, name); 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 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 data = -1; printk("%s.\r\n", __func__); data = vir_device_read(FALSE); memcpy(buf, &data, sizeof(int)); return sizeof(int); } 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); }
vir_peripheral_device.c源码:
/* * vir_peripheral_device.c * * Created on: Nov 28, 2020 * Author: Administrator */ #define __SYLIXOS_KERNEL #include <SylixOS.h> #include "vir_peripheral_device.h" #define READ_PERIOD (3) #define MUTEX_CREATE_DEF_FLAG \ LW_OPTION_WAIT_PRIORITY | \ LW_OPTION_INHERIT_PRIORITY | \ LW_OPTION_DELETE_SAFE | \ LW_OPTION_OBJECT_GLOBAL static LW_HANDLE tid; static LW_HANDLE read_sync; static BOOL readable = FALSE; static int data = 1; static void *vdev_thread (void *sarg) { while (1) { sleep(READ_PERIOD); data++; readable = TRUE; API_SemaphoreBPost(read_sync); } return NULL; } int vir_device_init (void) { LW_CLASS_THREADATTR threadattr; API_ThreadAttrBuild(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); tid = API_ThreadCreate("t_vdev", vdev_thread, &threadattr, LW_NULL); if (tid == LW_OBJECT_HANDLE_INVALID) { return (PX_ERROR); } read_sync = API_SemaphoreBCreate("count_lock", 0, LW_OPTION_OBJECT_LOCAL, LW_NULL); if (read_sync == LW_OBJECT_HANDLE_INVALID) { API_ThreadDelete(&tid, NULL); printk("semaphore create failed.\n"); return (PX_ERROR); } return ERROR_NONE; } int vir_device_deinit (void) { API_ThreadDelete(&tid, NULL); return ERROR_NONE; } int vir_device_read (BOOL non_block) { if (non_block) { if (readable) { goto return_data; } else { return 0; } } API_SemaphoreBPend(read_sync, LW_OPTION_WAIT_INFINITE); return_data: readable = FALSE; return data; }
vir_peripheral_device.h源码:
/* * vir_peripheral_device.h * * Created on: Nov 28, 2020 * Author: Administrator */ #ifndef SRC_VIR_PERIPHERAL_DEVICE_H_ #define SRC_VIR_PERIPHERAL_DEVICE_H_ int vir_device_init(void); int vir_device_deinit(void); int vir_device_read (BOOL non_block); #endif /* SRC_VIR_PERIPHERAL_DEVICE_H_ */
app_demo10.c源码:
#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; } while (1) { ret = read(fd, &data, 4); if (ret < 0) { printf("read data fail.\r\n"); close(fd); return -1; } else if (ret == 0) { 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日 12:14 1F
写的真好!
2023年4月23日 11:09 2F
非常不错的教材
2023年7月12日 11:46 3F
结构清晰,讲解详细,折服