經過上一節的介紹,我們知道在 linux 中進行 C語言開發時,多進程同時寫數據到同一個文件,如果不小心處理,寫入的數據可能會混亂。這主要是因為每個進程打開文件時,都有獨立的文件表記錄當前文件偏移量的原因。
那麼,在一個進程中多次打開同一個文件同時寫入數據,也可能出現數據混亂嗎?答案是肯定的,還記得第10節的這張圖嗎?
即使在一個進程中多次調用 open 函數打開同一個文件,系統也會為每次調用分配一個文件表記錄當前文件偏移量。這時,情況就和上一節介紹的多進程同時寫數據到同一個文件的情況類似了,還是非常可能出現數據混亂的。
請看下面的代碼:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> int main() { char* filename = "test.bin"; int fd, fd2; fd = open(filename, O_WRONLY|O_CREAT); fd2 = open(filename, O_WRONLY|O_CREAT); char buf1[20] ; char buf2[30] ; memset(buf1, 1, sizeof(buf1)); memset(buf2, 2, sizeof(buf2)); write(fd, buf1, sizeof(buf1)); write(fd2, buf2, sizeof(buf2)); close(fd); close(fd2); return 0; }
代碼的邏輯很簡單,就是在 main 函數裡打開 test.bin 文件兩次,并分别往裡面寫入 20 個字節的 1,和 30 個字節的 2。編譯執行之,得到如下結果:
容易看出,fd 寫入的數據被 fd2 寫入的數據覆蓋了,這與我們前面的分析是一緻的。
實際開發中,還是非常有可能需要打開同一個文件多次的,比如多線程項目中,每個線程都需要操作 test.bin 文件,這時使用多個 fd 更加方便。C語言的 dup 和 dup2 函數好在,C語言提供的 dup 和 dup2 函數,就非常适合解決多個 fd 寫數據到同一個文件的需求。dup 和 dup2 都可用來複制一個現存的文件描述符,使兩個文件描述符指向同一個文件表。
在 linux 中輸入 man 命令即可查詢 dup 函數的描述:
現在使用 dup 函數修改上面的代碼,複制一份 fd 傳遞給 fd2:
... fd = open(filename, O_WRONLY|O_CREAT); fd2 = dup(fd); char buf1[20] ; ...
修改以後,編譯執行,發現兩次寫入的數據都保留了。
這是因為 fd2 是由 dup 函數複制 fd 而來,它倆共享同一個文件表,也即共享同一個當前文件偏移量。前面兩節介紹過,fd 調用 write 寫入數據後,會将當前文件偏移量更新,這時 fd2 也就接着 fd 寫入的數據尾部寫入數據了。
再看看上面的代碼,雖然 fd2 是複制 fd 而來的,但是仍需調用 close 函數關閉之。所以如果隻執行第 26 行代碼,linux 内核隻會将 test.bin 文件的打開計數減一,并沒有真正關閉文件。因此,第 27 行的 close(fd2); 是必需的。
這也能看出多線程操作同一個文件,使用 dup 的好處了。某個線程使用完文件後,直接 close 即可(這能使代碼有更好的邏輯完整性,可閱讀性更強),而無需擔心其他線程。dup 函數複制 fd 時,總是返回盡可能小的未使用 fd 号。dup2 函數與 dup 函數的功能時類似的,唯一的區别是 dup2 函數有兩個參數,執行成功後,會返回第二個參數傳遞的 fd 值。
多進程打開同一個文件時,能利用 dup 函數避免數據紊亂嗎?
既然 dup 函數能夠複制 fd,那麼,上一節出現的問題也能用 dup 函數的特性解決嗎?這個問題就留給讀者思考了。經過這兩節的讨論,相信讀者也有能力編寫相應的 C語言代碼驗證自己的想法。
這裡有一點小提示:進程間通常并不共享内存,而進程打開文件時,文件表信息保留在自己的内存空間裡的。歡迎在評論區一起讨論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!