• 所谓阻塞,就是等待某件事情发生。比如调用 read 读取按键时,如果没有按键数据则 read 函数不会返回,它会让线程休眠等待。
    • 使用 poll 时,如果传入的超时时间不为 0,这种访问方法也是阻塞的。
    • 使用 poll 时,可以设置超时时间为 0,这样即使没有数据它也会立刻返回,这就是非阻塞方式。
    • 能不能让 read 函数既能工作于阻塞方式,也可以工作于非阻塞方式?可以!
      • APP 调用 open 函数时,传入 O_NONBLOCK,就表示要使用非阻塞方式;默认是阻塞方式。

    注意:对于普通文件、块设备文件,O_NONBLOCK 不起作用。
    注意:对于字符设备文件,O_NONBLOCK 起作用的前提是驱动程序针对 O_NONBLOCK 做了处理。

    • 只能在 open 时表明 O_NONBLOCK 吗?在 open 之后,也可以通过 fcntl 修改为阻塞或非阻塞
    • image.png
    • image.png
      • file结构体里有flags标记位,用来保存打开驱动程序时传入的flag,或者在fcntl修改flag时传入的新值
    • 这样驱动程序中,read对flag的阻塞和非阻塞进行处理即可

      1. /* 实现对应的open/read/write等函数,填入file_operations结构体 */
      2. static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
      3. {
      4. //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
      5. int err;
      6. int key;
      7. /* 判断是否位NONBLOCK */
      8. if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
      9. return -EAGAIN;
      10. /* 否则认为是阻塞,调用wait_event(有数据就返回,没有则休眠) */
      11. wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
      12. key = get_key();
      13. err = copy_to_user(buf, &key, 4);
      14. return 4;
      15. }
    • 应用编程

      1. fd = open(argv[1], O_RDWR | O_NONBLOCK);
      2. if (fd == -1)
      3. {
      4. printf("can not open file %s\n", argv[1]);
      5. return -1;
      6. }
      7. for (i = 0; i < 10; i++)
      8. {
      9. if (read(fd, &val, 4) == 4)
      10. printf("get button: 0x%x\n", val);
      11. else
      12. printf("get button: -1\n");
      13. }
      14. flags = fcntl(fd, F_GETFL);
      15. fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
      16. while (1)
      17. {
      18. if (read(fd, &val, 4) == 4)
      19. printf("get button: 0x%x\n", val);
      20. else
      21. printf("while get button: -1\n");
      22. }
    • 当 APP 打开某个驱动时,在内核中会有一个 struct file 结构体对应这个驱动,这个结构体中有 f_flags,就是打开文件时的标记位;可以设置 f_flasgs 的 O_NONBLOCK 位,表示非阻塞;也可以清除这个位表示阻塞。

    • 驱动程序要根据这个标记位决定事件未就绪时是休眠和还是立刻返回