Linux下的非阻塞IO(一)

非阻塞IO是相对于传统的阻塞IO而言的。

我们首先需要搞清楚,什么是阻塞IO。APUE指出,系统调用分为两类,低速系统调用和其他,其中低速系统调用是可能会使进程永远阻塞的一类系统调用。但是与磁盘IO有关的系统调用是个例外。

我们以read和write为例,read函数读取stdin,如果是阻塞IO,那么:

如果我们不输入数据,那么read函数会一直阻塞,一直到我们输入数据为止。

如果是非阻塞IO,那么:

如果存在数据,读取然后返回,如果没有输入,那么直接返回-1,errno置为EAGAIN

我们用write做一个实验:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>

char buf[500000];

int main(int argc, const char *argv[])
{
    int ntowrite, nwrite;

    ntowrite = read(STDIN_FILENO, buf, sizeof buf);
    fprintf(stderr, "read %d bytes\n", ntowrite);

    activate_nonblock(STDOUT_FILENO, O_NONBLOCK);

    char *ptr = buf;
    int nleft = ntowrite; //剩余的字节数
    while(nleft > 0)
    {
        errno = 0;
        nwrite = write(STDOUT_FILENO, ptr, nleft);
        fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);

        if(nwrite > 0)
        {
            ptr += nwrite;
            nleft -= nwrite;
        }
    }

    deactivate_nonblock(STDOUT_FILENO);

    return 0;
}

该程序向标准输出写入500000个字节。

如果使用:

./test < test.mkv > temp.file

那么输出结果为:

read 500000 bytes
nwrite = 500000, errno = 0

因为磁盘IO的速度较快,所以一次就可以写入,下面我们使用终端:

./test < test.mkv  2> stderr.txt

这行命令将500000的内容打印到屏幕上,同时将fprintf记录的信息通过标准错误流写入stderr.txt。

我们查看stderr.txt文件:

read 500000 bytes
nwrite = 12708, errno = 0
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = 11687, errno = 0
nwrite = -1, errno = 11
…………………………………………..

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = -1, errno = 11

nwrite = 1786, errno = 0

采用命令统计了一下,总计read次数为15247次,其中返回-1次数为15203次,说明成功读取次数为44次。

上面例子中,这种采用非阻塞IO的方式称为“轮询”,显然这是一种低效的方式,非阻塞IO通常与IO复用模型结合使用。

 

另外,将fd设置为阻塞和非阻塞的函数代码如下:

void activate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}

void deactivate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}

 

未完待续。

时间: 2024-08-07 09:09:05

Linux下的非阻塞IO(一)的相关文章

[Z] linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO

原文链接:http://blog.csdn.net/colzer/article/details/8169075 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据

linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据区,等一些属性).那么我们的应用程序对文件的读写就通过对描述符的读写完成. linux将内存分为内核区,用户区.l

linux函数的阻塞与非阻塞IO及错误处理

1.阻塞是指进程等待某一个事件的发生而处于等待状态不往下执行,如果等待的事件发生了则会继续执行该进程.调用系统阻塞函数可能会导致进程阻塞进入睡眠状态. 2.阻塞IO之read读取键盘输入数据 3.linux错误处理 ###函数perro();r打印括号里的字符串和错误信息,全局变量errno会被赋值当出现错误信息时,赋的值为下面的错误标号 ###错误标号及对应错误 #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /*

python(十)下:事件驱动与 阻塞IO、非阻塞IO、IO多路复用、异步IO

上节的问题: 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 一.事件驱动模型介绍 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求 第三种就是协程.时间驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 论事件驱动模型 在UI编程中,,常常要对鼠标点击进行相应,

Linux下文件的阻塞与非阻塞对部分系统调用的影响

1.基本概念 所谓的阻塞,即内核在对文件操作I/O系统调用时,如果条件不满足(可能需要产生I/O),则内核会将该进程挂起.非阻塞则是发现条件不满足就会立即返回.此外需要注意的是非阻塞并不是轮询,不然就和阻塞没多大区别了,它只是调用不成功就直接返回了,不会在去看啥时候会满足条件,而是有你自己去选择接下来该咋办,系统以不再负责 2.read/write阻塞与非阻塞的理解 read/write系统调用,并不会去直接读写文件,而只是去操作文件所对应的内存页(此时的页为虚拟内存),对于read如果在页中找

Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端

前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore/p/4049165.html中提出过,客户端需要监听stdin.stdout和sockfd. 这里需要注意的是 只有缓冲区可写的时候,才去监听sockfd和stdin的读事件. 过去在阻塞IO中,我们总是监听sockfd的读事件,因为每当sockfd可读,我们就去调用用户的回调函数处理read事件

Linux非阻塞IO(四)非阻塞IO中connect的实现

我们为客户端的编写再做一些工作. 这次我们使用非阻塞IO实现connect函数. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 非阻塞IO有以下用处: 1.将三次握手的处理过程生下来,处理其他事情. 2.使用这个同时建立多个连接. 3.实现超时connect功能,本节实现的connect就可以指定时间,超时后算作错误处理.   在阻塞IO中,调用connect后一般会阻塞,直到确定连接成功或者失败

Linux非阻塞IO(六)使用poll实现非阻塞的服务器端

关于poll模型监听的事件以及返回事件,我们定义宏如下: #define kReadEvent (POLLIN | POLLPRI) #define kWriteEvent (POLLOUT | POLLWRBAND) #define kReadREvent (POLLIN | POLLPRI | POLLRDHUP) #define kWriteREvent (POLLOUT) 前面我们说明了,为什么非阻塞IO必须具备缓冲区.事实上,对于server而言,每条TCP连接应该具有两个缓冲区,一个

《linux设备驱动开发详解》笔记——8阻塞与非阻塞IO

8.1 阻塞与非阻塞IO 8.1.0 概述 阻塞:访问设备时,若不能获取资源,则进程挂起,进入睡眠状态:也就是进入等待队列 非阻塞:不能获取资源时,不睡眠,要么退出.要么一直查询:直接退出且无资源时,返回-EAGAIN 阻塞进程的唤醒:必须有地方能够唤醒处于睡眠状态的阻塞进程,否则就真睡不醒了.一般是在中断中. 阻塞与非阻塞可以在open时设置,也可以通过fcntl和ioctl重新设置 8.1.1 等待队列 linux驱动中,可以用等待队列wait queue实现阻塞.等待队列与linux进程调