多进程下的文件描述符

多进程下的文件描述符

咱们都知道fork以后, 子进程继承了父进程的文件描述符. 可是这是什么意思? 若是父子进程或多个子进程同时操做这个文件描述符会如何?php

奇怪的表现

脚本的基本逻辑是父进程读取要处理的文件是计算行数, 而后根据配置的子进程个数fork子进程,子进程处理某一范围的数据,好比子进程1处理1到10行, 子进程2处理11到20. 由于对这个文件的处理是只读操做, 因此没有进行文件拆分,而是直接使用继承的文件描述符, 各子进程移动到本身处理的范围进行处理.后端

可是在测试的时候, 发现有些子进程老是没法读到数据, strace 调试发现read(2)返回0. 可是增长fseek以后,又正常了, 见当时代码注释:数组

//开发时发现若是没有这个fseek, 会致使$proc_idx=0的那个子进程fgets读不到数据,strace显示read返回0
    //缘由不明
    fseek($order_file, ftell($order_file));
    while(($line_no <= $stop) && $line = fgets($order_file, 4096))
    {
        ....
    }

能够看到这个fseek到ftell彻底是没有用的,但加了这句这后就就正常了. 真是匪夷所思.socket

开发时, 一开始是正常的, 可是加上了经过socket调用后端服务以后, 就异常了, 去掉此socket则不须要fseek也能够正常读到数据.ide

我一度还觉得是PHP的BUG,因而更新了PHP的版本, 问题仍是如此.测试

某一时刻突然灵光一闪, read不到数据, 能够是文件偏移量不对.... 或者当时灵光是另外一种闪法也未可知, 老是我因而去查看fork, 获得如下信息:翻译

man 2 fork
...
       *  The child inherits copies of the parent's set of open file descriptors.  Each file descriptor in  the  child
          refers  to  the same open file description (see open(2)) as the corresponding file descriptor in the parent.
          This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O
          attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).
...

因而再查open调试

man 2 open
       A call to open() creates a new open file description, an entry in the system-wide table of  open  files.   This
       entry  records  the  file  offset and the file status flags (modifiable via the fcntl(2) F_SETFL operation).  A
       file descriptor is a reference to one of these entries;

请注意这里有两个词file descriptorsfile description. 回到一开始咱们所说的"咱们都知道...文件描述符...", file descriptorsfile description均可翻译为"文件描述符", 但实际上两个是不一样的. file descriptors 能够认为是数组的下标 file descriptors才是正常的数据, 这个数据包括了file offset, fork以后子进程继承的是file descriptors(下标), 但下标指向的是 同一个file description, 因此你们都在read这个file descriptors, 你们都修改了同一个fiel offset, 会相互影响.code

test code

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

void child(int idx, int fd);

int main(int argc, char const* argv[])
{
    int i;
    int pid;
    int fd = open(argv[1], O_RDONLY);

    for (i = 0; i < 3; i++)
    {
        pid = fork();
        if(0 == pid)
        {
            //child 
            child(i, fd);
            exit(0);
        }
    }
    return 0;
}

void child(int idx, int fd)
{
    FILE* fp            = 0;
    char filename[1024] = {0};
    char s[1024]        = {0};
    snprintf(filename, sizeof(filename), "test_%d.txt", idx);

    fp = fopen(filename, "wb");
    while(read(fd, s, idx * 10 + 1))
    {
        fwrite(s, sizeof(char), idx*10 + 1, fp);
    }
}

按我原来的理解, 若是继承的"文件描述符"是互不影响的, 那么三个子进程都能复制到同一份完整的文件,但事实上几乎每一个子进程都读到错乱的文件.继承

解决方法

各子进程自行打开同一文件, 各自处理本身的范围

elseif($pid == 0)
        {
            //child
            process_file_range($task_id, $result_files['order_input_file'], $i, $child_proc_range[$i]["start"], $child_proc_range[$i]["stop"]);
            exit(0);
        }
=>
        elseif($pid == 0)
        {
            //child
            $result_files['order_input_file'] = fopen($order_file, 'r');
            process_file_range($task_id, $result_files['order_input_file'], $i, $child_proc_range[$i]["start"], $child_proc_range[$i]["stop"]);
            exit(0);
        }
相关文章
相关标签/搜索