【问题标题】:is it possible to read and write with the same file descriptor in C是否可以在 C 中使用相同的文件描述符进行读写
【发布时间】:2021-09-03 11:07:15
【问题描述】:

我正在尝试写入文件并显示我使用另一个进程编写的内容的输出。我想出的代码:

void readLine (int fd, char *str) {
    int n;
    do { 
        n = read (fd, str, 1);
    } while (*str++ != '\0');
}

int main(int argc,char ** argv){
    int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
    if(fork()==0){
        char buf[1000];
        while(1) {
            readLine(fd,buf);
            printf("%s\n",buf); 
        }
    }else{
        while(1){
             sleep(1);
             write(fd,"abcd",strlen("abcd")+1);
        }
    }
}

我想要的输出(每个结果之间的间隔为一秒):

abcd
abcd
abcd
....

不幸的是,这段代码不起作用,似乎子进程(文件“sharedFile”的读取器)从文件中读取垃圾,因为即使文件为空,它也会以某种方式读取值。 在尝试调试代码时,readLine 函数永远不会正确读取写入的文件,它总是读取 0 字节。 有人可以帮忙吗?

【问题讨论】:

  • 请仔细查看操作系统编程手册中read的各种可能返回值的含义。

标签: c file concurrency


【解决方案1】:

首先,当一个文件描述符在分叉后共享时,父子节点都指向相同的打开文件描述,这意味着它们共享相同的文件位置。这在fork() 手册页中有说明。

因此,每当父级写入时,位置都会更新到文件末尾,因此子级始终尝试在没有数据的文件末尾读取。这就是为什么read() 返回 0 的原因,就像您点击文件末尾时一样。

(发生这种情况时,您不应尝试对缓冲区中的数据做任何事情。这并不是说您在“阅读垃圾”,而是您没有阅读 任何内容,而是然后假装缓冲区中的任何垃圾都是您刚刚阅读的内容。特别是您的代码完全无视来自read()的返回值,这就是您应该告诉您实际阅读的内容的方式。)

如果你想让孩子有一个独立的文件位置,那么孩子需要为自己单独open()该文件并获得一个指向新文件描述的新fd。

但是,当孩子读取了当前文件中的所有数据后,read() 将再次返回 0;它不会等待父母再写一些。其他进程有一个文件打开以供写入这一事实不会影响常规文件上read() 的语义。

所以你需要做的是,当read() 返回 0 时,你手动休眠一段时间,然后重试。当文件中有更多数据时,read() 将返回一个正数,然后您可以处理您读取的数据。或者,有更优雅但更复杂的方法使用特定于系统的 API,如 Linux 的inotify,它可以休眠直到文件的内容发生变化。您可能熟悉tail -f,它在不同的系统上使用了这些方法的某种组合。

另一个危险的错误是,如果其他人将文本写入预期不包含空字节的文件,您的孩子将读取超出缓冲区容量的数据,从而超出缓冲区。这可能是一个可利用的安全漏洞。

这是修复这些错误并为我工作的代码版本:

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

void readLine (int fd, char *str, size_t max) {
    size_t pos = 0;
    while (pos < max) {
        ssize_t n = read(fd, str + pos, 1);
        if (n == 0) {
            sleep(1);
        } else if (n == 1) {
            if (str[pos] == '\0') {
                return;
            }
            pos++;
        } else {
            perror("read() failure");
            exit(2);
        }
    }
    fprintf(stderr, "Didn't receive null terminator in time\n");
    exit(2);
}

int main(int argc, char ** argv){
    int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
    if (fd < 0) {
        perror("parent opening sharedFile");
        exit(2);
    }
    pid_t pid = fork();
    if (pid == 0){
        int newfd = open("sharedFile", O_RDONLY);
        if (newfd < 0) {
            perror("child opening sharedFile");
            exit(2);
        }
        char buf[1000];
        while (1) {
            readLine(newfd, buf, 1000);
            printf("%s\n",buf); 
        }
    } else if (pid > 0) {
        while (1){
            sleep(1);
            write(fd,"abcd",strlen("abcd")+1);
        }
    } else {
        perror("fork");
        exit(2);
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 2018-11-29
    相关资源
    最近更新 更多