直接上代码:

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

int main() {
    FILE *f1 = fopen("./sysv_msgque.cpp", "r");
    printf("lseek():%d\n", ftell(f1));
    FILE *f2 = fopen("./sysv_msgque.cpp", "r"); // f1、f2相互独立
    printf("lseek():%d\n", ftell(f2));

    char buffer[256];
    int r_size;
    r_size = fread(buffer, 1, 10, f1);
    printf("%s\n", buffer);
    r_size = fread(buffer, 1, 5, f2);
    printf("%s\n", buffer);
    printf("lseek():%d\n", ftell(f1));
    printf("lseek():%d\n", ftell(f2));

    int o1 = open("./sysv_msgque.cpp", O_RDONLY);
    int o2 = open("./sysv_msgque.cpp", O_RDONLY);
    int o3 = dup(o2);  // o1、o2之间互相不影响,o3、o2之间共用一个文件表项,共享文件位移量
    memset(buffer, 0, 256);
    lseek(o1 , 5 , SEEK_SET);
    read(o1, buffer, 1);
    printf("%s\n", buffer);
    memset(buffer, 0, 256);
    read(o2, buffer, 1);
    printf("%s\n", buffer);
    memset(buffer, 0, 256);
    read(o3, buffer, 1);
    printf("%s\n", buffer);
}

测试结果是:每一次打开的文件描述符(即使打开相同文件)之间使用的是不同的文件表项,由dup函数复制返回的两个文件描述符才会使用相同的文件表项,共享各类值。

 

引用自 https://blog.csdn.net/qq_28114615/article/details/94590598 :

 

1. 每次调用open或者create(内部实际上还是调用的open),都会对新打开的文件分配一个file结构体,并且将打开文件的标志、状态、权限等信息填入这个file结构体中。这个file结构体也叫文件表项;

2. 磁盘中的每个文件都对应一个i-node,每一个文件表项都会指向一个文件的i-node,但是同一文件的i-node可以对应多个文件表项(当多次调用open打开同一个文件时就会出现这种情况,不管是同一进程多次打开同一文件(如图中A进程的0号和2号文件描述符对应两个文件表项,但是最终指向同一i-node即同一文件),还是不同进程多次打开同一文件(如图中A进程3号文件描述符和B进程的3号文件描述符));

3.  同一进程下的不同文件描述符是可以指向同一文件表项,即最终指向同一文件;

4. 子进程在创建时会拷贝父进程的打开文件描述符表,因此父子进程是共享文件表项的(系统级),也就是父子进程的读写会影响对方的文件偏移量;

5. 指向同一文件表项的不同文件描述符共享文件标志、文件偏移等信息;

6. 对于指向同一文件表项的两个不同文件描述符,即使其中一个文件描述符关闭了,只要仍然有文件描述符指向这个文件表项,那么就依然能通过这个文件表项访问文件,直到所有指向该文件表项的文件描述符都关闭了才不能再进行访问;

 

 

fwrite和write对比,先说结论:

1. fwrite有缓存,write没有缓存。所以fwrite写入更快,而且比write快不少。但是如果涉及到子进程需要小心,fwrite还未写入文件的缓存会被子进程继承,最后会导致文件内容错误。

2. fwrite和write都是覆盖式的写入。要实现文件那种插入式写入,要自己处理。

3. fwrite不是原子性的,write是原子性的。

    int fd = open("./write_file", O_CREAT | O_WRONLY, S_IREAD|S_IWRITE);
    int target_lines = 1500000;

    int pid = fork();
    if (pid == 0) {
        // int fd = open("./write_file", O_CREAT | O_WRONLY, S_IREAD|S_IWRITE);
        sleep(1);
        char buf[512] = "bbbb\n";
        for (int i = 0; i < target_lines; i++) {
            int res = write(fd, buf, strlen(buf));
            if (res != strlen(buf)) {
                printf("child wrong");
                exit(0);
            }
        }
    } else {
        // int fd = open("./write_file", O_CREAT | O_WRONLY, S_IREAD|S_IWRITE);
        sleep(1);
        char buf[512] = "aaa\n";
        for (int i = 0; i < target_lines; i++) {
            int res = write(fd, buf, strlen(buf));
            if (res != strlen(buf)) {
                printf("parent wrong");
                exit(0);
            }
        }
    }

 类似上面这种测试,用fwrite结果错误,用write正常。就是因为fwrite不是原子操作,子进程父进程同时写入,会相互覆盖。如果要在fwrite基础上构建原子性操作,需要自己使用flock之类的加锁操作。

4. write原子性是POSIX标准的要求,但是在https://man7.org/linux/man-pages/man2/write.2.html看到一段描述:

BUGS         top
       According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread
       Interactions with Regular File Operations"):

           All of the following functions shall be atomic with respect
           to each other in the effects specified in POSIX.1-2008 when
           they operate on regular files or symbolic links: ...

       Among the APIs subsequently listed are write() and writev(2).
       And among the effects that should be atomic across threads (and
       processes) are updates of the file offset.  However, on Linux
       before version 3.14, this was not the case: if two processes that
       share an open file description (see open(2)) perform a write()
       (or writev(2)) at the same time, then the I/O operations were not
       atomic with respect to updating the file offset, with the result
       that the blocks of data output by the two processes might
       (incorrectly) overlap.  This problem was fixed in Linux 3.14.

所以又做了点实验,发现在centos7里面write没有冲突。

最后:log内决定使用write,效率靠后~~

 

posted on 2021-03-31 14:04  SimbaStar  阅读(1642)  评论(0编辑  收藏  举报