错误地写入同步进程的文件输出顺序?

问题描述:

我有以下问题。错误地写入同步进程的文件输出顺序?

我正在与信号灯同步的两个过程和思路是这样的:

  • 过程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 

为什么会发生这种情况?

此外,在进程写入文件之前,我总是锁定它,以便其他进程无法访问它并再次解锁。

我不确定我是否做得对,所以我会很感激我能得到的任何建议!

非常感谢!

默认情况下,您已经缓冲了IO - 因此,如果缓冲区的输出小于缓冲区大小,那么缓冲区实际上并不会被刷新到该文件,直到您关闭它为止(否则您会在缓冲区已满时获得缓冲区的值。经常8kb左右)。还要注意,每个进程都有自己独立的缓冲区。

要修复,请在打印完成后刷新输出,并在写入之前将fseek()更改为SEEK_END以确保您位于文件末尾。

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

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

+0

另请参阅https://www.quora.com/In-C-what-does-buffering-IO-or-buffered-IO-mean – JimmyB

+0

我编辑了如下代码:fseek(fp,0,SEEK_END) ; fprintf(fp,“proc1 - action1 \ n”); fflush(fp);它似乎在工作。我做对了吗?非常感谢您的信息!请问,文件锁定怎么办?我是否正确地使用每个写入操作来锁定和解锁文件?我正在考虑是否有更优雅的方式来做这件事,因为当我可以说,10在每个进程中写这样的动作时,代码很快就变得乱七八糟。 – Daeto

fp = fopen("output.txt", "w");截断文件和不同步,所以进程#2可能会截断文件,即使进程#1已经写了一些东西。 fp = fdopen(fd, "w");

另外,如果在其他进程写入文件时保持打开文件,那么您将面临麻烦。在第二个过程完成其写入之后,至少应将SEEK添加到文件的(新)结尾,否则您可能会覆盖#2所执行的过程。在其他进程运行之前更好地关闭文件,然后重新打开。这也将确保所有缓冲数据都被刷新。