咱們都知道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 descriptors與file description. 回到一開始咱們所說的"咱們都知道...文件描述符...", file descriptors與file description均可翻譯爲"文件描述符", 但實際上兩個是不一樣的. file descriptors 能夠認爲是數組的下標 file descriptors纔是正常的數據, 這個數據包括了file offset, fork以後子進程繼承的是file descriptors(下標), 但下標指向的是 同一個file description, 因此你們都在read這個file descriptors, 你們都修改了同一個fiel offset, 會相互影響.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); }