【问题标题】:Wrong write to file output order of synchronized processes?同步进程的文件输出顺序错误?
【发布时间】:2016-04-25 15:00:59
【问题描述】:

我有以下问题。

我有两个与信号量同步的进程,想法是这样的:

  • 进程 1 向 txt 文件写入内容
  • 进程 2 向 txt 文件写入内容
  • 进程 1 向测试文件写入内容

我已经包含了演示问题的示例代码:

// semaphore names 
#define NAME1 "/s1"
#define NAME2 "/s2" 

int main()
{
    /* semaphores for process synchronization */ 
    sem_t *sm1;
    sm1 = sem_open( NAME1, O_CREAT, 0666, 0);

    sem_t *sm2;
    sm2 = sem_open(NAME2, O_CREAT, 0666, 0);

    /* processes*/
    int proc1;
    int proc2;

    /* file lock struct */ 
    struct flock fl = {F_WRLCK, SEEK_SET,   0,      0,     0 };

    /* create a text file */ 
    FILE *fp;
    int fd;

    fp = fopen("output.txt", "w"); // create and close the file 
    fclose(fp);

    if((fd = open("output.txt", O_RDWR)) == -1) { // open the file again to get file descriptor
        perror("open");
        exit(1);
    }

    fp = fdopen(fd, "w");

    /* first process */
    if ((proc1 = fork()) < 0) {
        perror("fork");
        exit(2);
    }
    else if(proc1 == 0) {
        fl.l_type = F_WRLCK; // set the lock type and pid of the forked process 
        fl.l_pid = getpid();

        if (fcntl(fd, F_SETLKW, &fl) == -1) { // lock the file before writing to it 
            perror("fcntl");
            exit(1);
        }

        fprintf(fp, "proc1 - action1\n"); // write to the file

        fl.l_type = F_UNLCK;  

        if (fcntl(fd, F_SETLK, &fl) == -1) {  // unlock the file so other processes can write to it
            perror("fcntl");
            exit(1);
        }

        fprintf(stdout, "proc1 - action1\n"); 

        sem_post(sm1); // let the second process run 
        sem_wait(sm2); // wait till the second process is done 

        // write one more thing the same way to the text file after the second process is done 
        fl.l_type = F_WRLCK;  
        fl.l_pid = getpid();

        if (fcntl(fd, F_SETLKW, &fl) == -1) { 
            perror("fcntl");
            exit(1);
        }

        fprintf(fp, "proc1 - action2\n"); 

        fl.l_type = F_UNLCK;  

        if (fcntl(fd, F_SETLK, &fl) == -1) {  
            perror("fcntl");
            exit(1);
        }

        fprintf(stdout, "proc1 - action2\n"); 

        exit(0);
    }

    /* second process */ 
    if ((proc2 = fork()) < 0) {
        perror("fork");
        exit(2);
    }
    else if(proc2 == 0) {
        sem_wait(sm1); // waits for proc1 to perform it's first action

        // write something to the text file and let proc1 write it's second action 
        fl.l_type = F_WRLCK;  
        fl.l_pid = getpid();

        if (fcntl(fd, F_SETLKW, &fl) == -1) { 
            perror("fcntl");
            exit(1);
        }

        fprintf(fp, "proc2 - action1\n"); 

        fl.l_type = F_UNLCK;  

        if (fcntl(fd, F_SETLK, &fl) == -1) {  
            perror("fcntl");
            exit(1);
        }

        fprintf(stdout, "proc2 - action1\n"); 

        sem_post(sm2);

        exit(0);
    }

    // wait for both processes to finish 
    waitpid(proc1, NULL, 0);
    waitpid(proc2, NULL, 0);

    sem_close(sm1);
    sem_unlink(NAME1);

    sem_close(sm2);
    sem_unlink(NAME2);

    return 0;
}

我已将 fprintf 包含在标准输出行中,以便您可以看到终端中的输出是正确的:

-proc1 - action1
-proc2 - action1
-proc1 - action2

就像它们正在同步一样。但是,在 output.txt 文件中的输出是这样的:

-proc2 - action1
-proc1 - action1
-proc1 - action2

为什么会这样?

另外,在进程写入文件之前,我总是锁定它,这样其他进程就无法访问它,然后再次解锁。

我不确定我这样做是否正确,所以如果我能得到任何建议,我将不胜感激!

非常感谢!

【问题讨论】:

    标签: c linux synchronization semaphore file-locking


    【解决方案1】:

    默认情况下,您已缓冲 IO - 因此,如果缓冲区的输出小于缓冲区大小,则缓冲区实际上不会刷新到文件,直到您关闭它(否则您会在缓冲区已满时获得缓冲区的价值.. . 通常是 8kb 左右)。另请注意,每个进程都有自己的独立缓冲区。

    要修复,请在打印之后和写入之前刷新您的输出,fseek() 到 SEEK_END 以确保您位于文件末尾。

    fprintf(fp, "proc1 - action1\n"); // write to the file
    fflush(fp);
    

    顺便说一句,我没有检查你的信号量代码的有效性,只是注意到你错过了这条重要的信息。然而,一般来说,如果你使用文件锁定,信号量是多余的......

    【讨论】:

    • 我编辑了这样的代码:fseek(fp, 0, SEEK_END); fprintf(fp, "proc1 - action1\n"); fflush(fp);它似乎正在工作。我做对了吗?非常感谢您的信息!请问,文件锁怎么办?我是否在每次写入操作时都正确锁定和解锁文件?我在想是否有更优雅的方式来做到这一点,因为当我说,在每个进程中编写 10 个这样的操作时,代码很快就会变得一团糟。
    【解决方案2】:

    fp = fopen("output.txt", "w");截断文件并且不同步,因此进程#2 可能会截断文件,即使进程#1 已经写入了一些内容。 fp = fdopen(fd, "w");也一样

    此外,如果您在其他进程写入文件时保持文件打开,您将面临麻烦。至少您应该在第二个进程完成写入后将SEEK 放在文件的(新)末尾,否则您可能会覆盖进程#2 所做的。最好在其他进程运行之前关闭文件,然后再重新打开。这也将确保刷新所有缓冲数据。

    【讨论】:

      猜你喜欢
      • 2015-03-04
      • 1970-01-01
      • 2019-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多