fcntl() スコープロック(長さからの一部のオフセット)がfcntlフルファイルロック(EOFから0オフセット)よりも遅いのはなぜですか? [閉鎖]

fcntl() スコープロック(長さからの一部のオフセット)がfcntlフルファイルロック(EOFから0オフセット)よりも遅いのはなぜですか? [閉鎖]

同時に、8つの異なるプロセスから1 GBのファイルに書き込もうとします(各プロセスは、2つの異なるアプローチに基づいてオフセット0から始めて書き込みます)。

  1. 各プロセスは、ファイルの他の領域に書き込む場合でも、書き込み前にfcntl()を使用して完全なファイルロック(オフセット0 - EOF)を取得します。
  2. 各プロセスは、書き込み前にファイル内の対応する領域のロックを取得するためにfcntl()を使用します。

私が理解したところによると、2番目の方法は最初の方法よりも時間がかかりません。しかし、テストしてみると、最初の方法は時間がかかりませんでした。誰かが理由を説明できますか?

基本ファイルシステム: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);
}

2番目の方法

    #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

関連情報