在STDOUT_FILENO上使用fcntl F_SETLKW合法吗?

时间:2019-03-23 13:43:06

标签: c linux synchronization posix

我有多个进程通过主程序的fork生成,这意味着它们输出到相同的stdout(我想要的)。但是我需要以某种方式防止输出被交错。我已经使用fcntl锁来同步对日志文件的访问,因此我也想将其用于stdout。

然而,this博客文章声称fcntl锁是associated with an [i-node, pid] pair。 stdout是否有一个inode?我决定尝试一下

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>

void do_log(const char *msg) {
    printf("%5ld: %s\n", getpid(), msg);
}

void try_lock(void) {
    do_log("Trying to lock stdout...");

    struct flock fl = {
        .l_type = F_WRLCK,
        .l_whence = SEEK_SET,
        .l_start = 0,
        .l_len = 0,
    };
    if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1) {
        perror("fcntl - lock");
        exit(1);
    }

    do_log("Stdout locked");
    sleep(2);
    do_log("Trying to unlock stdout...");

    fl.l_type = F_UNLCK;
    if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1) {
        perror("fcntl - unlock");
        exit(1);
    }

    do_log("Stdout unlocked");
}

int main(void) {
    if (fork()) {
        try_lock();
    } else {
        try_lock();
    }
    wait(NULL);
    return 0;
}

似乎可行

17156: Trying to lock stdout...
17155: Trying to lock stdout...
17155: Stdout locked
17155: Trying to unlock stdout...
17155: Stdout unlocked
17156: Stdout locked
17156: Trying to unlock stdout...
17156: Stdout unlocked

但是在fcntl的联机帮助页中,我注意到If fildes refers to a typed memory object, the result of the fcntl() function is unspecified.,但我不知道那是什么意思。

所以我想我有两个问题:

  1. 是否在这样的stdout上使用fcntl锁保证可以正常工作?
  2. 如果对1.的回答为“是”,那么博客帖子中有关[i-node, pid]的信息是否错误,或者stdout实际上有一个i节点?

1 个答案:

答案 0 :(得分:1)

inode编号(以及设备编号)纯粹是文件的唯一标识符。在该术语的现代用法中,它与磁盘文件系统结构无关。所有文件(和文件描述符指的是打开文件的实例)都有一个索引节点编号,至少对它们执行fcntl有意义。但是,根据POSIX:

  

常规文件应支持记录锁定,其他文件也应支持

Linux可能在某些文件类型上不支持锁定。我不确定。

还请注意,锁定纯粹是建议-如果进行写操作的进程在执行访问之前未尝试获取锁定,则锁定无效。

通过共享内存进行锁定可能更有意义,尤其是在使用fork而没有exec的情况下。在分叉之前,mmap一个MAP_ANON|MAP_SHARED区域并在其中设置进程共享的互斥量。如果任何进程可能会意外死机,则可以使其成为强大的互斥体。保证可以正常工作,并且也应该更快,因为除了锁争用之外,它纯粹是一个用户空间操作。