嵌入式学习——3——IO分类模型

1、阻塞IO和非阻塞IO

1.1 阻塞IO

 - 在阻塞IO模型中,当一个IO操作(如读取或写入)开始时,如果数据没有准备好,程序会被挂起(即阻塞),直到数据准备好并且IO操作完成。

   - 在数据准备阶段,程序做不了其他事情,它会等待在那里直到整个IO操作完全完成后才会继续执行。

   - 阻塞IO简化了编程模型,因为在发起IO请求之后,你只需要等待结果,不必担心在这个过程中如何处理其他任务。

1.2 非阻塞IO

 - 非阻塞IO模型中,IO操作不会导致程序挂起等待。如果数据没有准备好,IO调用会立即返回一个指示数据尚未准备好的状态。

   - 程序可以继续执行后续代码,它可以定期地检查IO操作是否可以进行,或者可以进行其他工作,这样很好地利用了等待时间。该过程称为 polling 。

   - 非阻塞IO通常与事件驱动或轮询机制一起使用,程序可以在多个IO操作之间有效地切换,提高了程序的整体效率和响应性。

非阻塞IO更适用于处理高并发情况,尤其在需要同时管理多个连接或者进行多个并行IO操作时。但这种模型编程难度较高,因为要管理和调度多个IO操作,而且可能需要额外的机制(如IO复用或事件通知)来检测IO状态。

1.3 如何将阻塞IO更改成非阻塞IO

#include <unistd.h>

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

功能:获取或者设置当前文件描述符的属性和状态,是哪一种操作取决于cmd

fd:要处理的文件描述符

cmd: 要执行的操作

        F_GETFL:  获取文件描述符的属性,第三个参数可以忽略

        F_SETFL:设置文件描述符的属性,第三个参数为int

是否设置参数3,取决于参数2,如果参数2为 F_SETFL,参数3就是为当前文件描述符设置的值

返回值:取决于cmd

        如果cmd 为F_GETFL,则成功返回当前文件描述符的属性值,失败返回-1并置位错误码

        如果cmd 为 F_SETFL,则成功返回0,失败返回-1并置位错误码

步骤:

1、先获取当前文件描述符的属性

        int flag = fcntl(fd, F_GETFL);

2、将当前的文件描述符的属性中,添加非阻塞属性

        flag |= O_NONBLOCK;

3、将更改后的状态加到当前文件描述符中

        fcntl(fd, F_SETFL, flag);

int main(int argc, const char *argv[])
{
    int num = 0;

    //1、获取当前文件描述的属性
    int flag = fcntl(0, F_GETFL);

    //2、给当前的文件描述符加上非阻塞属性
    flag |= O_NONBLOCK;

    //3、再将新属性设置回去
    fcntl(0, F_SETFL, flag);

    while(1)
    {
        printf("请输入:");
        scanf("%d", &num);

        //此处省略一万行代码
        printf("num = %d\n", num);
    }

    return 0;
}

2、IO多路复用

2.1 IO多路复用的相关概念

1、如果程序中需要同时处理多个阻塞任务时,在使用单进程或单线程的情况下,可以处理多个阻塞任务

2、在无法使用多进程或多线程时,还想完成多任务并发,此时可以使用IO多路复用技术

3、优点:由于不需要多进程或多线程,能够有效减少系统的资源开销,减少上下文切换的次数,效率较高

4、原理图

2.2 select

#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

功能:阻塞等待检测容器中是否有事件产生,如果有一个或多个事件产生,则解除阻塞,并且将没有产生事件的文件描述符从集合中清除

参数1:检测集合中最大的文件描述符的值 + 1

参数2、3、4:分别是读、写、异常检测的文件描述符集合,如果不用,可以填NULL

参数5:超时时间,可以是如下的结构体变量的地址,也可以填NULL,表示永久等待

struct timeval {

        long tv_sec; /* seconds */

        long tv_usec; /* microseconds */

};

and

struct timespec {

        long tv_sec; /* seconds */

        long tv_nsec; /* nanoseconds */

};

返回值: 

        >0     表示本次解除阻塞触发事件的文件描述符总个数

        =0     表示设置的超时时间,并超时时间内没有事件产生

        =-1    出错,置位错误码

四个宏函数,用于对文件描述符集合的操作

        void FD_CLR(int fd, fd_set *set);         // 将fd文件描述符从集合set中移除

        int FD_ISSET(int fd, fd_set *set);         // 判断fd文件描述符是否存在于set集合中

        void FD_SET(int fd, fd_set *set);         // 将fd文件描述符添加到set集合中

        void FD_ZERO(fd_set *set);         // 将set文件描述符集合清空

使用select实现TCP并发服务器--框架

int main(){

        sfd = socket();      // 创建用于连接的套接字

        bind();                   // 绑定ip和端口号

        listen();                 // 将套接字设置成被动监听状态

        fd_set readfds, tempfds;

        FD_ZERO(&readfds)

        FD_SET(sfd, &readfds);

        int max = sfd;

        while(1){

                tempfds = readfds;

                select(max+1, &tempfds, NULL, NULL,NULL); //阻塞检测集合中文件描述符

                                                                                                是否有事件产生

                for(int i = 0; i <= max; i++){

                        if(!FD_ISSET(i, &tempfds)){

                                continue;

                        }

                        if(i == sfd){

                                newfd = accept()

                                FD_SET(newfd ,&readfds );

                                max = max > newfd ? max : newfd;

                        }else{

                                recv()

                                send()

                                (下线关闭)close(i)

                                FD_CLR(i, &readfds);

                                //更新max

                                 for(j = max; j >= sfd; j--){

                                        if(FD_ISSET(j, &tempfds)){

                                                max = j;

                                        }

                                }

                        }

                }

        }

        close(sfd);

}

2.3 poll

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:阻塞等待文件描述符集合中是否有事件产生,如果有事件产生,则解除阻塞

参数1:文件描述符集合数组,是一个结构体数组

                struct pollfd {

                        int fd; /* 要检测的文件描述符 */

                        short events; /* 要检测的事件 */

                        short revents; /* 真实发生的事件 */  该值无需初始化,poll函数会给

                                                        该参数赋值

                };

常用事件种类:

          POLLIN:读事件

          POLLOUT:写事件

参数2:集合中文件描述符的个数

参数3:以毫秒为单位的超时时间

                >0:        表示设置的超时时间

                =0:      表示非阻塞

                <0:        表示永久阻塞

返回值:

                >0:        表示返回解除阻塞的事件对应的文件描述符个数

                =0:        表示超时

                <0:        失败,置位错误码

poll完成TCP客户端中发送数据和接收数据的并发执行

int main(){

        cfd = socket();

        bind()

        connect();

        // 使用poll 完成 多阻塞任务的并发

        struct pollfd pfd[2];               //  定义一个2长度的结构体数组  

        pfd[0].fd = 0;                        //  表示检测0号(stdin)文件描述符

        pfd[0].events = POLLIN;      //  表示检测事件

        

        pfd[1].fd = cfd;                      //  表示检测cfd文件描述符

        pfd[1].events = POLLIN;       //  表示检测事件

        

        while(1){

                int res = poll(pfd, 2, -1);

                if (res == -1){

                        perror("poll error");

                        return -1;

                }else if (res == 0){

                        printf("time out\n");

                        return -1;

                }

                //判断是否为0号文件描述符产生的事件

                if(pfd[0].revents == POLLIN){

                }

                //判断是否为cfd文件描述符产生事件

                 if(pfd[1].revents == POLLIN){

                        recv()

                }

        }        

        close(cfd)

         return 0;

}

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值