You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash中stdout输出异常:Linux I/O程序运行卡顿问题

Linux I/O程序卡顿问题排查与修复

问题背景

学习Linux I/O编程时编写了一段从标准输入读取内容并输出到标准输出的代码,在CLion调试模式下运行正常,但在Bash终端中输入几次后程序出现卡顿。使用的rio_前缀函数来自《Computer Systems: A Programmer's Perspective》一书。

主程序代码

int main(int argc,char **argv){
    int n;
    int acc= 0;
    rio_t rio;
    char buf[MAXLINE];
    char *readp = buf;

    rio_readinitb(&rio,STDIN_FILENO);
    while((n= rio_readlineb(&rio,readp,MAXLINE)) != 0){
        readp = readp +n;
        acc += n;
        write(STDOUT_FILENO,"Outputs\n",8);
        write(STDOUT_FILENO,"--------\n",9);
        rio_writen(STDOUT_FILENO,buf,acc);
    }
}

相关rio_函数实现代码

ssize_t rio_writen(int fd,void *usrbuf,size_t n){
    size_t nleft = n;
    ssize_t nwritten;
    char* bufp = usrbuf;

    while(nleft >0){
        if((nwritten = write(fd,bufp,nleft)) <= 0 ){
            if (errno = EINTR){ // 错误:赋值而非比较
                nwritten = 0;
            }else{
                return -1;
            }
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

// Buffered I/O
void rio_readinitb(rio_t *rp,int fd){
    rp-> rio_fd = fd;
    rp-> rio_cnt = 0;
    rp-> rio_bufptr = rp->rio_buf;
}

static ssize_t rio_read(rio_t *rp,char *usrbuf,size_t n){
    /* Refill if buffer is empty*/
    while(rp->rio_cnt <= 0){
        rp->rio_cnt = read(rp->rio_fd,rp->rio_buf,sizeof(rp->rio_buf));

        if(rp->rio_cnt <0){
            if(errno != EINTR){
                return -1;
            }
        } else if(rp->rio_cnt == 0){
            return 0;
        }else {
            rp->rio_bufptr = rp->rio_buf;
        }
    }

    size_t cnt = rp->rio_cnt;
    if(n < rp->rio_cnt){
        cnt = n;
    }
    memcpy(usrbuf,rp->rio_bufptr,cnt);
    rp-> rio_bufptr += cnt;
    rp->rio_cnt -= cnt;

    return cnt;
}

ssize_t rio_readlineb(rio_t *rp,void *usrbuf,size_t maxlen){
    int n,rc;
    char* bufp = usrbuf;
    char c;

    for(n = 0;n<maxlen;n++){
        if((rc= rio_read(rp,&c,1))== 1){
            *bufp++ = c;
            if(c == '\n'){
                n++;
                break;
            }
        }else if(rc == 0){
            if(n==0){
                return 0;
            } else{
                break;
            }
        }else{
            return -1;
        }
    }
    *bufp = 0;
    return n;
}

ssize_t rio_readbnb(rio_t *rp,void* usrbuf,size_t n){
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;

    while(nleft > 0){
        if(nread = rio_read(rp,bufp,n) < 0){ // 错误:运算符优先级问题
            if (errno == EINTR){
                nread = 0;
            }else{
                return -1;
            }
        }else if(nread == 0){
            break;
        }
        nleft -= nread;
        bufp += nread;
    }

    return (n-nleft);
}

问题原因分析

1. 主程序缓冲区溢出

主程序中buf是固定大小的栈数组(MAXLINE),但readp持续向后移动、acc持续累加读取的字节数,多次输入后readp会超出buf的边界,触发栈缓冲区溢出。调试模式下CLion通常会启用栈保护机制(如-fstack-protector),能阻止程序崩溃,但在Bash直接运行时无此保护,导致未定义行为(表现为卡顿、崩溃或异常)。

2. rio_writen函数的逻辑错误

rio_writen中errno = EINTR是赋值操作而非比较操作,导致errno被错误设置为EINTR,后续错误处理逻辑完全失效。

3. rio_readbnb函数的运算符优先级问题

nread = rio_read(rp,bufp,n) < 0中,<的优先级高于=,实际执行顺序是nread = (rio_read(...) < 0),nread会被赋值为0或1(比较结果),而非实际读取的字节数,导致读取逻辑混乱。

修复方案

修复主程序(解决缓冲区溢出)

每次处理完一行后,直接输出当前读取的行内容,避免持续占用缓冲区:

#include "csapp.h" // 假设包含rio_t定义和MAXLINE常量

int main(int argc,char **argv){
    int n;
    rio_t rio;
    char buf[MAXLINE];

    rio_readinitb(&rio,STDIN_FILENO);
    while((n= rio_readlineb(&rio,buf,MAXLINE)) != 0){
        write(STDOUT_FILENO,"Outputs\n",8);
        write(STDOUT_FILENO,"--------\n",9);
        rio_writen(STDOUT_FILENO,buf,n); // 直接输出当前读取的n字节
    }
}

修复rio_writen函数

将赋值操作改为比较操作:

ssize_t rio_writen(int fd,void *usrbuf,size_t n){
    size_t nleft = n;
    ssize_t nwritten;
    char* bufp = usrbuf;

    while(nleft >0){
        if((nwritten = write(fd,bufp,nleft)) <= 0 ){
            if (errno == EINTR){ // 修正为比较操作
                nwritten = 0;
            }else{
                return -1;
            }
        }
        nleft -= nwritten;
        bufp += nwritten;
    }
    return n;
}

修复rio_readbnb函数

给赋值表达式添加括号,确保正确的执行顺序:

ssize_t rio_readbnb(rio_t *rp,void* usrbuf,size_t n){
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;

    while(nleft > 0){
        if((nread = rio_read(rp,bufp,n)) < 0){ // 添加括号修正优先级
            if (errno == EINTR){
                nread = 0;
            }else{
                return -1;
            }
        }else if(nread == 0){
            break;
        }
        nleft -= nread;
        bufp += nread;
    }

    return (n-nleft);
}

验证

修复后,在Bash终端运行程序,多次输入内容将正常输出,不会出现卡顿或异常。

内容的提问来源于stack exchange,提问作者H.Okan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:28:10