Linux中的BIO和NIO

Linux中的BIO和NIO

1. 概念

  1. BIO(阻塞IO)

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

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

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

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

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

    void ioctl(...);
    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成对使用)

void sleep_on(queue);
void interruptible_sleep_on(queue);

3. 轮询操作

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

  2. select()、poll()在文件量增大时,性能降低

    //任一文件变得可读、可写、异常时返回
    int select(
        //监听的文件的最高fd+1
        int numfds, 
        //监听的读文件
        fd_set *readfds,
        //监听的写文件
        fd_set *writefds,
        //监听的异常文件
        fd_set *exceptfds,
        //监听超时
        struct timeval *timeout
    );
    //下面的宏用于操作fd_set文件描述符集合
    //清空
    FD_ZERO(fd_set *set);
    //添加
    FD_SET(int fd, fd_set *set);
    //删除
    FD_CLR(int fd, fd_set *set);
    //判断
    FD_ISSET(int fd, fd_set *set);
    
    //poll操作与select操作类似
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    
  3. epoll()是poll()的扩展,是一种事件驱动的操作

  4. epoll不会由于fd的数量变大而降低性能

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值