Linux中的BIO和NIO

梦里梦外; 2022-09-11 15:20 281阅读 0赞

Linux中的BIO和NIO

1. 概念

  1. BIO(阻塞IO)

    执行某个操作时,若不能获得资源,则挂起进程知道满足条件获取资源后再执行。挂起进程的唤醒一般发生在中断里,因为硬件资源的获取一般伴随着一个中断

    1. //以阻塞的方式从串口读取一个字符
    2. char buf[20];
    3. fd = open("/dev/ttyS1", O_RDWR);
    4. //阻塞读取字符
    5. res = read(fd, &buf, 1);
    6. printf("%c\n", buf);
  2. NIO(非阻塞IO)

    执行某个操作时,若不能获得资源,不会挂起进行,要么放弃,要么不停的查询,直到可以进行操作为止

    1. //以非阻塞的方式从串口读取一个字符
    2. char buf[20];
    3. fd = open("/dev/ttyS1", O_RDWR | O_NONBLOCK);
    4. //读取字符,读取操作立即返回,所以需要循环读取
    5. while(read(fd, &buf, 1) == -1)
    6. continue;
    7. printf("%c\n", buf);
  3. 改变文件的读写方式

    除了可以在文件打开时定义读写方式外还可以通过ioctl()和fcntl()改变读写方式。

    1. void ioctl(...);
    2. void fcntl(...);

2. 等待队列

等待队列是BIO的底层实现方式

  • 等待队列与Linux内核的进程调度紧密结合
  • 信号量是依赖于等待队列实现的

    //定义等待队列
    wait_queue_head_t queue;
    //初始化头
    void init_waitqueue_head(&queue);
    //定义+初始化头
    DECLARE_WAIT_QUEUE_HEAD(name);
    //定义元素
    DECLARE_WAITQUEUE(name, tsk);
    //添加到队列
    void add_wait_queue(wait_queue_head_t q, wait_queue_t wait);
    //从队列移除
    void remove_wait_queue(wait_queue_head_t q, wait_queue_t wait);
    //等待事件
    void wait_event(queue, condition);
    void wait_event_timeout(queue, condition);
    void wait_event_interruptible(queue, condition);
    void wait_event_interruptible_timeout(queue, condition);
    //唤醒队列
    void wake_up(queue);
    void wake_up_interruptible(queue);

在等待队列中休眠(与wake_up成对使用)

  1. void sleep_on(queue);
  2. void interruptible_sleep_on(queue);

3. 轮询操作

  1. select()、poll()系统调用都是BIO中查询文件的方式,一次可以查询多个文件描述符,其中任一个变得可写或可读时返回。是一种多路复用的思想
  2. select()、poll()在文件量增大时,性能降低

    1. //任一文件变得可读、可写、异常时返回
    2. int select(
    3. //监听的文件的最高fd+1
    4. int numfds,
    5. //监听的读文件
    6. fd_set *readfds,
    7. //监听的写文件
    8. fd_set *writefds,
    9. //监听的异常文件
    10. fd_set *exceptfds,
    11. //监听超时
    12. struct timeval *timeout
    13. );
    14. //下面的宏用于操作fd_set文件描述符集合
    15. //清空
    16. FD_ZERO(fd_set *set);
    17. //添加
    18. FD_SET(int fd, fd_set *set);
    19. //删除
    20. FD_CLR(int fd, fd_set *set);
    21. //判断
    22. FD_ISSET(int fd, fd_set *set);
    23. //poll操作与select操作类似
    24. int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  3. epoll()是poll()的扩展,是一种事件驱动的操作
  4. epoll不会由于fd的数量变大而降低性能

    1. //创建epoll,设置监听的fd数量,epoll本身占用一个fd
    2. int epoll_create(int size);
    3. //关闭epoll fd
    4. void epoll_close();
    5. //等待事件产生,events是输出(从内核得到的事件集合),maxevents是本次最多接收的事件数
    6. //timeout是超时(毫秒),-1表示永久
    7. //返回本次接收到的事件数
    8. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    9. //设置epoll监听的fd
    10. //参数op: EPOLL_CTL_ADD 注册新的fd到epfd中
    11. //参数op: EPOLL_CTL_MOD 修改已注册的fd的监听事件
    12. //参数op: EPOLL_CTL_ADD 从epfd中删除一个fd
    13. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    14. //struct epoll_event是监听的事件类型
    15. struct epoll_event {
    16. //EPOLLIN 可读
    17. //EPOLLOUT 可写
    18. //EPOLLPRI 有紧急数据可读(socket紧急数据)
    19. //EPOLLERR fd发生错误
    20. //EPOLLHUP fd被挂断
    21. //EPOLLONESHOT 一次性监听,一次监听事件完成后,需要再次把fd加入epfd中
    22. //EPOLLET epoll设为边缘触发模式(Edge Triggered)
    23. //默认是水平触发模式(Level Triggered)
    24. //ET是高速模式,当fd从非就绪变为就绪时,不会发送就绪通知
    25. __uint32_t events;
    26. epoll_data_t data;
    27. };
  5. 少量fd的查询用select、poll,大量fd的查询用epoll

发表评论

表情:
评论列表 (有 0 条评论,281人围观)

还没有评论,来说两句吧...

相关阅读

    相关 BIONIOAIO

    BIO(Blocking I/O)同步阻塞I/O 这是最基本与简单的I/O操作方式,其根本特性是做完一件事再去做另一件事,一件事一定要等前一件事做完,这很符合程序员传统的顺

    相关 NIOBIO

      BIO 同步阻塞式IO,服务器实现模式:为一个连接建立一个线程,即客户端有连接请求时,服务器端就需要启动一个线程进行处理,这个线程和这个连接就捆绑到了一起,线程就等

    相关 BIONIOAIO

    IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。 一、BIO      在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要