linux系统编程-read()write()函数分析

本文记录我对read(),write()函数这两个常见调用的再次学习。参考了如下链接:
[socket:阻塞与非阻塞模式下,read和write的返回值 ]

read

阻塞调用:

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 128

void set_fl( int fd, int flag ){
    int ret;

    if( -1 == (ret = fcntl(fd, F_GETFL, 0)) ){
        perror( "fcntl()" );
        exit( EXIT_FAILURE );
    }

    ret |= flag;

    if( -1 == ( ret = fcntl(fd, F_SETFL, flag) ) ){ // 直接设置会关闭以前的标志位,所以需要先获取当前的标志为,然后修改,再置位
        perror( "fcntl()" );
        exit(EXIT_FAILURE);
    }
}

void set_nonblocking( int fd ) {
    set_fl( fd, O_NONBLOCK );
}

int main( void ){

    int ret;
    char buf[LEN];

    int fd = STDIN_FILENO;
    int nread = 0;
    while(1){
        nread = read( fd, buf, LEN-1 );
        if( nread > 0 ){ //  读取正常
            buf[nread-1] = '\0';
            printf( "Input from keyboard: %s\n", buf );
        }
        else if( 0 == nread ){ // fd关闭,读到FIN。或者EOF
            printf( "No more data!\n" );
            break;
        }
        else{
            // 读取异常
            if( EINTR == errno)//操作被中断,fd正常,可以继续读取
                continue;
            else{
                perror("read()");
                exit(EXIT_FAILURE);
            }
        }
    }


    exit(EXIT_SUCCESS);
}

非阻塞调用:

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 128

void set_fl( int fd, int flag ){
    int ret;

    if( -1 == (ret = fcntl(fd, F_GETFL, 0)) ){
        perror( "fcntl()" );
        exit( EXIT_FAILURE );
    }

    ret |= flag;

    if( -1 == ( ret = fcntl(fd, F_SETFL, flag) ) ){ // 直接设置会关闭以前的标志位,所以需要先获取当前的标志为,然后修改,再置位
        perror( "fcntl()" );
        exit(EXIT_FAILURE);
    }
}

void set_nonblocking( int fd ) {
    set_fl( fd, O_NONBLOCK );
}

int main( void ){

    int ret;
    char buf[LEN];

    int fd = STDIN_FILENO;
    int nread = 0;
    set_nonblocking(fd);

    while(1){
        nread = read( fd, buf, LEN-1 );
        if( nread > 0 ){ //  读取正常
            buf[nread-1] = '\0';
            printf( "Input from keyboard: %s\n", buf );
        }
        else if( 0 == nread ){ // fd关闭,读到FIN。或者EOF
            printf( "No more data!\n" );
            break;
        }
        else{
            // 读取异常
            if( EINTR == errno)//操作被中断,fd正常,可以继续读取
                continue;
            else if( EAGAIN == errno || EWOULDBLOCK == errno ){
                printf( "No data! sleep for a while.\n" );
                sleep(1);
            }
            else{
                perror("read()");
                exit(EXIT_FAILURE);
            }
        }
    }


    exit(EXIT_SUCCESS);
}

阻塞与非阻塞的区别,看起来是缓冲区为空是否返回。本质我们知道,I/O的第一个阶段是否阻塞。非阻塞模式,内核没有准备好数据就返回了。所以,需要判断,是不是数据没有准备好返回的。

write

select和non-blocking IO联合使用

Under Linux, select() may report a socket file descriptor as “ready for reading”, while nevertheless a subsequent read blocks.
This could for example happen when data has arrived but upon examination has wrong checksum and is discarded. There may be other
circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that
should not block.

上面这一段是man手册当中给出的内容,也就是说select通常要和non blocking IO一起使用。原因也很明显,我们之前总结过,多路复用是费阻塞io把轮询的过程帅锅给内核,那么当select,epoll这类调用返回的时候,一定是有socket就绪的。此时,在进行read是没有问题的。但是,man 手册举了个例子是,如果已经有数据就绪,然后select返回。但是,这个数据在传输过程中出了错误,进行丢弃。那么,在之后的读取时便会阻塞。因为,通常的情形,select报告事件发生后,进行读取是一定不会阻塞的。所以,不用将socket设为阻塞状态。但是,存在上面说的特殊的情形。数据就绪,但是有错误,进行丢弃。再读取时会阻塞。那么,此时进程被阻塞之后,无法执行select过程,也就是说,哪怕有别的socket就绪,也无法读取了。因为,不只是监听你一个socket。所以,多路复用的时候一定要小心。一定要和non blocking IO联合使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值