fseek() 与 fork() 无法正常工作

fseek() with fork() not working properly

我在结合使用 fseek() 和 fork() 时遇到问题(实际上使用的是 XUbuntu 15.10)。 我必须编写一个程序,从文件 ("file1") 中读取一系列数字(在不同的行中),并报告与特定字符串匹配的行数,作为参数传递。
我调用的程序 "nf" 必须按如下方式调用 (./nf number_of_processes file1 match_string).
该程序应该创建 number_of_processes 个子进程(使用 fork()),并且每个子进程都应该处理文件的一个部分(即如果 number_of_processes 是 5 并且文件有 15 行,每个子进程应该处理文件的 15/5=3 行)。
然后子进程应将结果报告给父进程,父进程将打印文件中出现的次数。

现在,问题是:我使用 fseek 编写了程序(每个子进程在文件中找到它的正确位置并开始分析它的单个部分的长度),但有时它似乎工作,而其他一些它打印不正确的结果,就像它以不正确的方式读取文件一样(多次读取或读取垃圾而不是数字字符串)...
你知道为什么会这样吗?
非常感谢。

这些文件如下:
文件 1:

1224332
1224332
4363666
4363666
1224332
5445774
2145515
1224332
2145515
1111111
2145515
9789899
2344444
6520031
4363666

nf.c:

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

#define NBYTES 8

int FileLenght(FILE *fp) {
    int cnt=0;
    char c;

    while((c=getc(fp))!=EOF) {
        if(c=='\n') {
            cnt++;
        }
    }

    rewind(fp);

    return cnt;
}

int main (int argc, char *argv[]) {
    int num,vlen=0,i,j=0,cnt=0;
    pid_t *pid;
    int status,sum=0;
    FILE *fp;
    char string[NBYTES+1];

    if(argc!=4) {
        printf("Error using program.\n");
        exit(EXIT_FAILURE);
    }

    num=atoi(argv[1]);

    fp=fopen(argv[2],"r+");

    if(!fp) {
        fprintf(stderr,"Error opening file.\n");
        exit(EXIT_FAILURE);
    }

    vlen=FileLenght(fp);

    pid=malloc(num*sizeof(pid_t));

    for(i=0;i<num;i++) {
        if(!(pid[i]=fork())) {
            fseek(fp,i*(NBYTES)*(vlen/num),SEEK_SET);
            while(j<vlen/num) {
                fscanf(fp,"%s",string);
                printf("Process %d reading from file: %s\n",getpid(),string);
                if(!strcmp(string,argv[3])) {
                    cnt++;
                }
                j++;
                printf("(%d-%d) %d %s=%s\n",getpid(),getppid(),j,string,argv[3]);
            }
            fclose(fp);
            exit(cnt);
        }
    }

    fseek(fp,vlen*NBYTES,SEEK_SET);

    for(i=0;i<num;i++) {
        waitpid(pid[i],&status,0);
        sum+=WEXITSTATUS(status);
    }

    printf("\nTotal found: %d\n",sum);

    fclose(fp);
    free(pid);

    return 0;
}

输出(正确的计数应该是 4 而不是 5):

$ ./nf 5 file1 1224332
Process 18592 reading from file: 1224332
Process 18593 reading from file: 4363666
(18593-18591) 1 4363666=1224332
Process 18593 reading from file: 4363666
(18593-18591) 2 4363666=1224332
(18592-18591) 1 1224332=1224332
Process 18592 reading from file: 1224332
(18592-18591) 2 1224332=1224332
Process 18594 reading from file: 1224332
Process 18596 reading from file: ���ҿ�
(18594-18591) 1 1224332=1224332
Process 18595 reading from file: 2145515
(18595-18591) 1 2145515=1224332
Process 18595 reading from file: 1224332
(18595-18591) 2 1224332=1224332
(18596-18591) 1 ���ҿ�=1224332
Process 18596 reading from file: ���ҿ�
Process 18594 reading from file: 1224332
(18594-18591) 2 1224332=1224332
(18596-18591) 2 ���ҿ�=1224332

Total found: 5

当文件描述符由于 fork() 而在进程之间共享时,文件描述符的所有属性都在副本之间共享,包括当前偏移量。因此,您程序中的所有子进程都试图同时从同一个文件描述符中查找和读取数据,从而导致各种意外结果。

要解决这个问题,您需要延迟打开文件直到您已经分叉,或者dup()分叉后的文件描述符为每个子进程创建一个独立的副本。

可能是每次创建子线程时指针fp.Because造成的,子线程都使用同一个fp,所以有时打印4,有时打印5,有时打印3,也2.