为什么 fcntl() 范围锁定(长度的一些偏移量)比 fcntl 完整文件锁定(EOF 的 0 偏移量)慢?

为什么 fcntl() 范围锁定(长度的一些偏移量)比 fcntl 完整文件锁定(EOF 的 0 偏移量)慢?

我试图同时从 8 个不同的进程写入 1 GB 文件(每个进程将从第 0 个偏移量开始写入直到 EOF);同时遵循两种不同的方法。

  1. 在写入之前,每个进程都将使用 fcntl() 获取整个文件锁(第 0 个偏移量 - EOF),即使它们正在文件的不同区域写入。
  2. 在写入之前,每个进程将仅使用 fcntl() 获取文件各自区域的锁。

据我了解,与第一种方法相比,第二种方法应该花费更少的时间。但在我的测试中,我发现第一种方法花费的时间更少。有人可以解释一下为什么吗?

底层文件系统:ext4

第一种方法

#define MAX_WRITE 65536

void write_file (char *path) {
        int fd;
        char buf[MAX_WRITE];
        ssize_t bytes_write;
        off64_t file_end, off;
        struct flock rl;

        fd = open64(path, O_WRONLY);
        if (fd == -1) {
                perror("open fail");
        }
        srand(time(0));
        char ch = (rand() % (33 - 122 + 1)) + 33;
        int i;
        for (i = 0; i < MAX_WRITE - 3; i += 4) {
                buf[i] = ch;
                buf[i+1] = ch;
                buf[i+2] = ch;
                buf[i+3] = ch;
        }
        buf[i+3] = '\n';
        file_end = lseek64(fd, 0, SEEK_END);
        lseek64(fd, 0, SEEK_SET);
        rl.l_type = F_WRLCK;
        rl.l_whence = SEEK_SET;
        rl.l_start = 0;
        rl.l_len = file_end;
        rl.l_pid = 0;
        for (off = 0; off < file_end; off+=bytes_write) {
                if (fcntl(fd, F_SETLKW, &rl) == -1) {
                        perror("fcntl locking failed");
                        exit(-1);
                }

                bytes_write = write(fd, buf, MAX_WRITE);
                if (bytes_write == -1) {
                        perror("Error writing file");
                        exit(-1);
                } else if (bytes_write < MAX_WRITE) {
                        printf("Partial write, only %ld no. of bytes are returned.\n",
                        bytes_write);
                }

                rl.l_type = F_UNLCK;
                if (fcntl(fd, F_SETLK, &rl) == -1) {
                        perror("fcntl locking failed");
                        exit(-1);
                }
        }
        printf("process %d Able to write %lld bytes..\n", getpid(), off);
        close(fd);
        return; 
}
int main (int argc, char **argv) {

        int fd, p_dead;
        if (argc < 3) {
                printf("Usage: %s <filename> <processes>\n", argv[0]);
                exit(-1);
        }
        char *path = argv[1];
        int p_no = atoi(argv[2]);

        /* Creating n number of processes */
        for (int i = 0; i < p_no; i++) {
                switch (fork()) {
                        case -1:
                                perror("fork failed:");
                                break;
                        case 0:
                                write_file(path);
                                exit(0);
                                break;
                        default:
                                /* Do nothing */
                                break;
                }
        }
        p_dead = 0;
        while (1) {
                if (wait(NULL) == -1) {
                        if (errno == ECHILD) {
                                printf("No child left..\n");
                                printf("Total no. of processes died : %d\n", p_dead);
                                exit(0);
                        } else {
                                perror("wait failed");
                                exit(-1);
                        }
                }
                p_dead++;
        }
        exit(0);
}

第二种方法

    #define MAX_WRITE 65536

void write_file (char *path) {
        int fd;
        char buf[MAX_WRITE];
        ssize_t bytes_write;
        off64_t file_end, off;
        struct flock rl;

        fd = open64(path, O_WRONLY);
        if (fd == -1) {
                perror("open fail");
        }
        srand(time(0));
        char ch = (rand() % (33 - 122 + 1)) + 33;
        int i;
        for (i = 0; i < MAX_WRITE - 3; i += 4) {
                buf[i] = ch;
                buf[i+1] = ch;
                buf[i+2] = ch;
                buf[i+3] = ch;
        }
        buf[i+3] = '\n';
        file_end = lseek64(fd, 0, SEEK_END);
        lseek64(fd, 0, SEEK_SET);
        rl.l_type = F_WRLCK;
        rl.l_whence = SEEK_CUR;
        rl.l_start = 0;
        rl.l_len = MAX_WRITE;
        rl.l_pid = 0;
        for (off = 0; off < file_end; off+=bytes_write) {
                if (fcntl(fd, F_SETLKW, &rl) == -1) {
                        perror("fcntl locking failed");
                        exit(-1);
                }
                bytes_write = write(fd, buf, MAX_WRITE);
                if (bytes_write == -1) {
                        perror("Error writing file");
                        exit(-1);
                } else if (bytes_write < MAX_WRITE) {
                        printf("Partial write, only %ld no. of bytes are returned.\n",
                        bytes_write);
                }
                rl.l_type = F_UNLCK;
                if (fcntl(fd, F_SETLK, &rl) == -1) {
                        perror("fcntl locking failed");
                        exit(-1);
                }
        }
        printf("process %d Able to write %lld bytes..\n", getpid(), off);
        close(fd);
        return;
}

int main (int argc, char **argv) {

        int fd, p_dead;
        if (argc < 3) {
                printf("Usage: %s <filename> <processes>\n", argv[0]);
                exit(-1);
        }
        char *path = argv[1];
        int p_no = atoi(argv[2]);

        /* Creating n number of processes */
        for (int i = 0; i < p_no; i++) {
                switch (fork()) {
                        case -1:
                                perror("fork failed:");
                                break;
                        case 0:
                                write_file(path);
                                exit(0);
                                break;
                        default:
                                /* Do nothing */
                                break;
                }
        }
        p_dead = 0;
        while (1) {
                if (wait(NULL) == -1) {
                        if (errno == ECHILD) {
                                printf("No child left..\n");
                                printf("Total no. of processes died : %d\n", p_dead);
                                exit(0);
                        } else {
                                perror("wait failed");
                                exit(-1);
                        }
                }
                p_dead++;
        }
        exit(0);
}

答案1

您正在通过覆盖来重复rl使用解锁,并且您不会在循环中重置。您的两个示例仅锁定第一次循环迭代。l_typeF_UNLCKl_type

相关内容