【问题标题】:Program gets stuck while trying to read a file using read() system call程序在尝试使用 read() 系统调用读取文件时卡住
【发布时间】:2016-10-14 20:29:47
【问题描述】:

这是我的代码 sn-p:

int fd;
bufsize = 30;
char buf[bufsize];
char cmd[100] = "file.txt";
int newfd = 1;

if (fd = open(cmd,O_RDONLY) >=0){
    puts("wanna read");
    while (read(fd,&bin_buf,bufsize)==1){
        puts("reading");
        write(newfd,&bin_buf,bufsize);
    }
    close(fd);
}

所以这里程序打印"wanna read",但从不打印"reading"。我也尝试过使用非阻塞标志打开,但没有用。有谁能够帮助我?我必须只使用open()read() 系统调用。谢谢。 编辑:我在代码中做了一些澄清。实际上,我正在写入的 newfd 是一个套接字描述符,但我认为这对于这个问题并不重要,因为它会粘在 read 上,它位于 write 之前。

【问题讨论】:

  • 那是因为它在读取的字节数为1时打印“正在读取”,但是您请求了bufsize字节。
  • 您只需启用警告-Wall 并修复所有的警告。
  • @user3386109:我理解你的沮丧,我个人鄙视 C 编译器仍然默认禁用许多救生警告这一事实!我总是打开-Werror,但我承认这不应该是默认设置。
  • @chqrlie 是的,关于救生警告,我们同意!至于-Werror,新手应该需要它;)

标签: c linux file-io system system-calls


【解决方案1】:

第一个问题是您的if 声明。您忘记使用足够多的括号,因此如果 open() 有效,则读取尝试从文件描述符 1 读取,即标准 输出。如果那是你的终端(它可能是)在一个 Unix 机器上,那么它可以工作——尽管这可能是令人惊讶的;程序正在等待您输入内容。

修正:使用括号!

if ((fd = open(cmd, O_RDONLY)) >= 0)

分配是在比较之前而不是之后完成的。

我顺便观察到你没有显示你是如何设置cmd的,但是如果你看到“想读”的信息,那一定没问题。您没有显示 newfd 是如何初始化的;也许那也是1

您还遇到“read() 调用返回的内容”的问题。你可能需要:

int fd;
char buf[bufsize];
int newfd = 1;

if ((fd = open(cmd, O_RDONLY)) >= 0)
{
    puts("wanna read");
    int nbytes;   // ssize_t if you prefer
    while ((nbytes = read(fd, buf, sizeof(buf))) > 0)
    {
        puts("reading");
        write(newfd, buf, nbytes);
    }
    close(fd);
}

您可以通过使用原始 if 输入一些内容(“惊喜”或“终端文件描述符通常是可读和可写的”或其他内容)来展示我的主要观察结果,但我的循环体然后写在某处。

【讨论】:

    【解决方案2】:

    您的read() 调用尝试读取bufsize 字节并返回实际读取的字节数。除非bufsize ==,否则read() 不太可能返回1,因此该块几乎总是被跳过并且没有写入任何内容。

    还要注意if (fd = open(cmd, O_RDONLY) >= 0) 不正确,并将fd 设置为标准输出句柄1,如果文件存在,导致read 失败,因为标准输入很可能未打开以供读取.

    请注意,在某些环境中,使用 read 系统调用进行读取会很棘手,因为返回值 -1 可能是可重新启动的。

    这是一个改进的版本:

    int catenate_file(const char *cmd, int newfd, size_t bufsize) {
        int fd;
        char buf[bufsize];
    
        if ((fd = open(cmd, O_RDONLY)) >= 0) {
            puts("wanna read");
            ssize_t nc;
            while ((nc = read(fd, buf, bufsize)) != 0) {
                if (nc < 0) {
                    if (errno == EINTR)
                        continue;
                    else
                        break;
                }
                printf("read %zd bytes\n", nc);
                write(newfd, buf, nc);
            }
            close(fd);
            return 0;
        }
        return -1;
    }
    

    【讨论】:

    • 您复制了 if (fd = open(cmd, O_RDONLY) &gt;= 0) { 分配与比较优先级错误。其余的都可以,尽管我不完全确定测试所写的中断内容。我会选择更简单的while ((nc = ...) &gt; 0) 方法。
    • @JonathanLeffler:哎呀!我错过了那个!关于 EINTR 的东西,手册页仍然提到这一点:POSIX 允许在读取一些数据后中断的 read() 返回 -1(将 errno 设置为 EINTR)或返回已读取的字节数。 老实说,我不知道是否有任何信号可以中断这个循环,包括被忽略的信号,但为什么不安全地玩呢?
    • 除非您设置了信号处理程序(在此代码之外),否则问题将没有实际意义——程序无论如何都会被中断终止。 read() 的 POSIX (2008/2013) 规范非常明确:如果 read() 在读取任何数据之前被信号中断,则它应返回 -1 并将 errno 设置为 [EINTR]。如果read() 在成功读取一些数据后被信号中断,则它应返回读取的字节数。因此,您所指的手册页应该仍然不准确。跨度>
    【解决方案3】:

    read 返回从文件中读取的字节数,如果必须读取的文件的剩余部分短于 bufsize,则该字节数可以为 bufsize 或更小。

    在您的情况下,bufsize 很可能大于 1 并且文件大于 1 字节,因此 while 循环的条件被评估为假,代码被跳过到文件关闭的位置。

    你应该检查是否有更多字节要读取

    while( read(fd,&bin_buf,bufsize) > 0 ) {
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多