• Linux学习第24天:Linux 阻塞和非阻塞 IO 实验(二): 挂起


    Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


            为方便和上一节的衔接,在正式开始学习前,先把本节的思维导图引入:

    二、阻塞IO实验

    1.硬件原理图分析

    2.实验程序

    1. #define IMX6UIRQ_NAME "blockio" /* 名字 */
    2. //修改设备文件名字为“blockio”,当驱动程序加载成功以后就会在根文件系统中出现一个名为“/dev/blockio”的文件。
    1. wait_queue_head_t r_wait; /* 读等待队列头 */
    2. //在设备结构体中添加一个等待队列头 r_wait,因为在 Linux 驱动中处理阻塞 IO需要用到等待队列。
    1. /* 唤醒进程 */
    2. if(atomic_read(&dev->releasekey)) { /* 完成一次按键过程 */
    3. /* wake_up(&dev->r_wait); */
    4. wake_up_interruptible(&dev->r_wait);
    5. }
    6. //定时器中断处理函数执行,表示有按键按下,先在 107 行判断一下是否是一次有效的按键,如果是的话就通过 wake_up 或者 wake_up_interruptible 函数来唤醒等待队列r_wait。
    1. /* 初始化等待队列头 */
    2. init_waitqueue_head(&imx6uirq.r_wait);
    1. //采用等待事件来处理 read 的阻塞访问, wait_event_interruptible 函数等待
    2. //releasekey 有效,也就是有按键按下。如果按键没有按下的话进程就会进入休眠状态,因为采用
    3. //了 wait_event_interruptible 函数,因此进入休眠态的进程可以被信号打断。
    4. 200 #if 0
    5. 201 /* 加入等待队列,等待被唤醒,也就是有按键按下 */
    6. 202 ret = wait_event_interruptible(dev->r_wait,
    7. atomic_read(&dev->releasekey));
    8. 203 if (ret) {
    9. 204 goto wait_error;
    10. 205 }
    11. 206 #endif
    1. 208 DECLARE_WAITQUEUE(wait, current); /* 定义一个等待队列 */
    2. 209 if(atomic_read(&dev->releasekey) == 0) { /* 没有按键按下 */
    3. 210 add_wait_queue(&dev->r_wait, &wait); /* 添加到等待队列头 */
    4. 211 __set_current_state(TASK_INTERRUPTIBLE);/* 设置任务状态 */
    5. 212 schedule(); /* 进行一次任务切换 */
    6. 213 if(signal_pending(current)) { /* 判断是否为信号引起的唤醒 */
    7. 214 ret = -ERESTARTSYS;
    8. 215 goto wait_error;
    9. 216 }
    10. 217 __set_current_state(TASK_RUNNING); /*设置为运行状态 */
    11. 218 remove_wait_queue(&dev->r_wait, &wait); /*将等待队列移除 */

            首先使用 DECLARE_WAITQUEUE 宏定义一个等待队列,如果没有按键按下的话就使用 add_wait_queue 函数将当前任务的等待队列添加到等待队列头 r_wait 中。随后调用__set_current_state 函数设置当前进程的状态为 TASK_INTERRUPTIBLE,也就是可以被信
    号打断。接下来调用 schedule 函数进行一次任务切换,当前进程就会进入到休眠态。如果有按
    键按下,那么进入休眠态的进程就会唤醒,然后接着从休眠点开始运行。在这里也就是从第 213
    行开始运行,首先通过 signal_pending 函数判断一下进程是不是由信号唤醒的,如果是由信号
    唤醒的话就直接返回-ERESTARTSYS 这个错误码。如果不是由信号唤醒的(也就是被按键唤醒
    的)那么就在 217 行调用__set_current_state 函数将任务状态设置为 TASK_RUNNING,然后在
    218 行调用 remove_wait_queue 函数将进程从等待队列中删除。

    3.运行测试

    1)、编译驱动程序和测试APP

    ①、编译驱动程序
    1. 1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imxrel_imx_4.1.15_2.1.0_ga_alientek
    2. ......
    3. 4 obj-m := blockio.o//修改变量的值
    4. ......
    5. 11 clean:
    6. 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

    make -j32
    编译成功以后就会生成一个名为“blockio.ko”的驱动模块文件。


    ②、编译测试APP
    arm-linux-gnueabihf-gcc blockioApp.c -o blockioApp

    2)、运行测试

    1. depmod //第一次加载驱动的时候需要运行此命令
    2. modprobe blockio.ko //加载驱动

    驱动加载成功以后使用如下命令打开 blockioApp 这个测试 APP,并且以后台模式运行:
     

    ./blockioApp /dev/blockio &

            当按下 KEY0 按键以后 blockioApp 这个测试 APP 就会打印出按键值。输入“top”命令,查看 blockioAPP 这个应用 APP 的 CPU 使用率。

            使用“kill -9 PID”即可“杀死”指定 PID 的进程,例如杀死149进程命令为:kill -9 149

    三、非阻塞IO实验

    1.硬件原理图分析

    2.实验程序

    1)、驱动程序编写

    1. #define IMX6UIRQ_NAME "noblockio" /* 名字 */
    2. //修改设备文件名字为“noblockio”,当驱动程序加载成功以后就会在根文件系统
    3. //中出现一个名为“/dev/noblockio”的文件。
    1. if (filp->f_flags & O_NONBLOCK) { /* 非阻塞访问 */
    2. if(atomic_read(&dev->releasekey) == 0) /* 没有按键按下 */
    3. return -EAGAIN;

            判断是否为非阻塞式读取访问,如果是的话就判断按键是否有效,也就是判断一下有没有按键按下,如果没有的话就返回-EAGAIN。

    1. 235 /*
    2. 236 * @description : poll 函数,用于处理非阻塞访问
    3. 237 * @param - filp : 要打开的设备文件(文件描述符)
    4. 238 * @param - wait : 等待列表(poll_table)
    5. 239 * @return : 设备或者资源状态,
    6. 240 */
    7. 241 unsigned int imx6uirq_poll(struct file *filp,
    8. struct poll_table_struct *wait)
    9. 242 {
    10. 243 unsigned int mask = 0;
    11. 244 struct imx6uirq_dev *dev = (struct imx6uirq_dev *)
    12. filp->private_data;
    13. 245
    14. 246 poll_wait(filp, &dev->r_wait, wait);
    15. 247
    16. 248 if(atomic_read(&dev->releasekey)) { /* 按键按下 */
    17. 249 mask = POLLIN | POLLRDNORM; /* 返回 PLLIN */
    18. 250 }
    19. 251 return mask;
    20. 252 }

            imx6uirq_poll 函数就是 file_operations 驱动操作集中的 poll 函数,当应用程序调用 select 或者 poll 函数的时候 imx6uirq_poll 函数就会执行。第 246 行调用 poll_wait 函数将等待队列头添加到 poll_table 中,第 248~250 行判断按键是否有效,如果按键有效的话就向应用程序返回 POLLIN 这个事件,表示有数据可以读取。

    .poll = imx6uirq_poll,//设置 file_operations 的 poll 成员变量为 imx6uirq_poll。

    2)、测试APP编写

    1. 52 #if 0
    2. 53 /* 构造结构体 */
    3. 54 fds.fd = fd;
    4. 55 fds.events = POLLIN;
    5. 56
    6. 57 while (1) {
    7. 58 ret = poll(&fds, 1, 500);
    8. 59 if (ret) { /* 数据有效 */
    9. 60 ret = read(fd, &data, sizeof(data));
    10. 61 if(ret < 0) {
    11. 62 /* 读取错误 */
    12. 63 } else {
    13. 64 if(data)
    14. 65 printf("key value = %d \r\n", data);
    15. 66 }
    16. 67 } else if (ret == 0) { /* 超时 */
    17. 68 /* 用户自定义超时处理 */
    18. 69 } else if (ret < 0) { /* 错误 */
    19. 70 /* 用户自定义错误处理 */
    20. 71 }
    21. 72 }
    22. 73 #endif

            使用 poll 函数来实现非阻塞访问。

    1. 75 while (1) {
    2. 76 FD_ZERO(&readfds);
    3. 77 FD_SET(fd, &readfds);
    4. 78 /* 构造超时时间 */
    5. 79 timeout.tv_sec = 0;
    6. 80 timeout.tv_usec = 500000; /* 500ms */
    7. 81 ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
    8. 82 switch (ret) {
    9. 83 case 0: /* 超时 */
    10. 84 /* 用户自定义超时处理 */
    11. 85 break;
    12. 86 case -1: /* 错误 */
    13. 87 /* 用户自定义错误处理 */
    14. 88 break;
    15. 89 default: /* 可以读取数据 */
    16. 90 if(FD_ISSET(fd, &readfds)) {
    17. 91 ret = read(fd, &data, sizeof(data));
    18. 92 if (ret < 0) {
    19. 93 /* 读取错误 */
    20. 94 } else {
    21. 95 if (data)
    22. 96 printf("key value=%d\r\n", data);
    23. 97 }
    24. 98 }
    25. 99 break;
    26. 100 }
    27. 101 }

            使用 select 函数来实现非阻塞访问。



    3.运行测试

    1)、编译驱动程序和测试APP

    ①、编译驱动程序
    obj-m := noblockio.o

            make -j32编译成功以后就会生成一个名为“noblockio.ko”的驱动模块文件。

    ②、编译测试APP
    arm-linux-gnueabihf-gcc noblockioApp.c -o noblockioApp

            编译成功以后就会生成 noblcokioApp 这个应用程序。

    2)、运行测试

    1. depmod //第一次加载驱动的时候需要运行此命令
    2. modprobe noblockio.ko //加载驱动

    驱动加载成功以后使用如下命令打开 noblockioApp 这个测试 APP,并且以后台模式运行:
     

    ./noblockioApp /dev/noblockio &

            当按下 KEY0 按键以后 noblockioApp 这个测试 APP 就会打印出按键值。输入“top”命令,
    查看 noblockioAPP 这个应用 APP 的 CPU 使用率。

    四、总结

            本篇笔记为本节的后半部分,主要内容为阻塞IO和非阻塞IO的驱动开发实现。


    本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

  • 相关阅读:
    Cloudreve搭建云盘系统,并实现随时访问
    How to choose an industrial vacuum cleaner?
    10. 结束语
    同步云盘:理解云端数据的实时同步技术
    通过matlab实现水产养殖鱼类成熟度自动分析系统
    图书目录管理系统(C++)
    时隔3天,我终于理解了四个盘子的汉诺塔问题(Java实现)
    一条慢SQL拖死整个系统
    MySQL学习笔记26
    跟着实例学Go语言(四)
  • 原文地址:https://blog.csdn.net/jiage987450/article/details/134090610