Linux中國

UNIX 的怪東西

最近我在用我編寫的各種工具做更多 UNIX 下的事情,我遇到了兩個有趣的問題。這些都不是 「bug」,而是我沒想到的行為。

線程安全的 printf

我有一個 C 程序從磁碟讀取一些圖像,進行一些處理,並將有關這些圖像的輸出寫入 STDOUT。偽代碼:

for(imagefilename in images)
{
    results = process(imagefilename);
    printf(results);
}

對於每個圖像都是獨立處理的,因此我自然希望將處理任務分配在各個 CPU 之間以加快速度。我通常使用 fork(),所以我寫了這個:

for(child in children)
{
    pipe = create_pipe();
    worker(pipe);
}

// main parent process
for(imagefilename in images)
{
    write(pipe[i_image % N_children], imagefilename)
}

worker()
{
    while(1)
    {
        imagefilename = read(pipe);
        results = process(imagefilename);
        printf(results);
    }
}

這是正常的做法:我為 IPC 創建管道,並通過這些管道給子進程 worker 發送圖像名。每個 worker 能夠通過另一組管道將其結果寫回主進程,但這很痛苦,所以每個 worker 都直接寫入共享 STDOUT。這工作正常,但正如人們所預料的那樣,對 STDOUT 的寫入發生衝突,因此各種圖像的結果最終會混雜在一起。那很糟糕。我不想自己設置個鎖,但幸運的是 GNU libc 為它提供了函數:flockfile()。我把它們放進去了……但是沒有用!為什麼?因為 flockfile() 最終因為 fork() 的寫時複製行為而被限制在單個子進程中。即 fork()提供的額外安全性(與線程相比),這實際上最終破壞了鎖。

我沒有嘗試使用其他鎖機制(例如 pthread 互斥鎖),但我可以想像它們會遇到類似的問題。我想保持簡單,所以將輸出發送回父輸出是不可能的:這給程序員和運行程序的計算機製造了更多的工作。

解決方案:使用線程而不是 fork()。這有製造冗餘管道的好的副作用。最終的偽代碼:

for(children)
{
    pthread_create(worker, child_index);
}
for(children)
{
    pthread_join(child);
}

worker(child_index)
{
    for(i_image = child_index; i_image < N_images; i_image += N_children)
    {
        results = process(images[i_image]);
        flockfile(stdout);
        printf(results);
        funlockfile(stdout);
    }
}

這更簡單,如預期的那樣工作。我猜有時線程更好。

將部分讀取的文件傳遞給子進程

對於各種 vnlog 工具,我需要實現這個操作序列:

  1. 進程打開一個關閉 O_CLOEXEC 標誌的文件
  2. 進程讀取此文件的一部分(在 vnlog 的情況下直到圖例的末尾)
  3. 進程調用 exec() 以調用另一個程序來處理已經打開的文件的其餘部分

第二個程序可能需要命令行中的文件名而不是已打開的文件描述符,因為第二個程序可能自己調用 ​​open()。如果我傳遞文件名,這個新程序將重新打開文件,然後從頭開始讀取文件,而不是從原始程序停止的位置開始讀取。在我的程序上不可以這樣做,因此將文件名傳遞給第二個程序是行不通的。

所以我真的需要以某種方式傳遞已經打開的文件描述符。我在使用 Linux(其他操作系統可能在這裡表現不同),所以我理論上可以通過傳遞 /dev/fd/N 而不是文件名來實現。但事實證明這也不起作用。在 Linux上(再說一次,也許是特定於 Linux)對於普通文件 /dev/fd/N 是原始文件的符號鏈接。所以這最終做的是與傳遞文件名完全相同的事情。

但有一個臨時方案!如果我們正在讀取管道而不是文件,那麼沒有什麼可以符號鏈接,並且 /dev/fd/N 最終將原始管道傳遞給第二個進程,然後程序正常工作。我可以通過將上面的 open("filename") 更改為 popen("cat filename") 之類的東西來偽裝。呸!這真的是我們所能做到最好的嗎?這在 BSD 上看上去會怎麼樣?

via: http://notes.secretsauce.net/notes/2018/08/03_unix-curiosities.html

作者:Dima Kogan 選題:lujun9972 譯者:geekpi 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國