ファイル記述子がfork()システムコール間でコピー継承されるのをどのように防止しますか(もちろん、それを閉じることなく)?
単一のファイル記述子をfork()の子によって(コピー)継承されないとしてマークする方法を探しています。これはFD_CLOEXECのようなハックのようなものですが、フォーク用(必要に応じてFD_DONTINHERIT機能)。誰かこれをしましたか?またはこれを調べて、私が始めるためのヒントがありますか?
ありがとうございました
更新:
libcの__register_atfork を使用できます
__register_atfork(NULL, NULL, fdcleaner, NULL)
fork()が戻る直前に子のfdsを閉じます。ただし、fdsはまだコピーされているので、これは私にはばかげたハックのように聞こえます。質問は、不要なfdsの子でdup()-ingをスキップする方法です。
Fcntl(fd、F_SETFL、F_DONTINHERIT)が必要になるいくつかのシナリオを考えています。
fork()はイベントfd(epollなど)をコピーします。たとえば、FreeBSDはkqueue()イベントfdをKQUEUE_TYPEとしてマークしており、これらのタイプのfdはフォーク間でコピーされません(必要に応じて、kqueue fdsはコピーから明示的にスキップされます)。共有fdテーブルでフォークする必要がある子から使用する)
fork()は、CPUを集中的に使用するタスクを実行するために子をフォークするために100kの不要なfdsをコピーします(fork()の必要性は確率的に非常に低く、プログラマーは通常はしない何かのために子のプールを維持したくないと仮定します。 tが起こる)
コピーしたい記述子(0,1,2)もあれば、コピーしない記述子(ほとんど?)もあります。歴史的な理由から、完全なfdtableの複製がここにあると思いますが、おそらく間違っています。
この音はどれほどばかげていますか:
プログラムを検討する
#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
static int fds[NUMFDS];
clock_t t1;
static void cleanup(int i)
{
while(i-- >= 0) close(fds[i]);
}
void clk_start(void)
{
t1 = clock();
}
void clk_end(void)
{
double tix = (double)clock() - t1;
double sex = tix/CLOCKS_PER_SEC;
printf("fork_cost(%d fds)=%fticks(%f seconds)\n",
NUMFDS,tix,sex);
}
int main(int argc, char **argv)
{
pid_t pid;
int i;
__register_atfork(clk_start,clk_end,NULL,NULL);
for (i = 0; i < NUMFDS; i++) {
fds[i] = open("/dev/null",O_RDONLY);
if (fds[i] == -1) {
cleanup(i);
errx(EXIT_FAILURE,"open_fds:");
}
}
t1 = clock();
pid = fork();
if (pid < 0) {
errx(EXIT_FAILURE,"fork:");
}
if (pid == 0) {
cleanup(NUMFDS);
exit(0);
} else {
wait(&i);
cleanup(NUMFDS);
}
exit(0);
return 0;
}
もちろん、これを本当のベンチと見なすことはできませんが、とにかく:
root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100 fds)=0.000000ticks(0.000000 seconds)
real 0m0.004s
user 0m0.000s
sys 0m0.000s
root@pinkpony:/home/cia/dev/kqueue# gcc -DNUMFDS=100000 -o forkit forkit.c
root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100000 fds)=10000.000000ticks(0.010000 seconds)
real 0m0.287s
user 0m0.010s
sys 0m0.240s
root@pinkpony:/home/cia/dev/kqueue# gcc -DNUMFDS=100 -o forkit forkit.c
root@pinkpony:/home/cia/dev/kqueue# time ./forkit
fork_cost(100 fds)=0.000000ticks(0.000000 seconds)
real 0m0.004s
user 0m0.000s
sys 0m0.000s
forkitは、Dell Inspiron 1520 Intel(R)Core(TM)2 Duo CPU T7500 @ 2.20GHz、4GBRAMで動作しました。 Average_load = 0.00
いいえ。閉じる必要があるものがわかっているので、自分で閉じてください。
fork
関数を呼び出す目的でexec
を実行する場合は、fcntl
とFD_CLOEXEC
を使用して、exec
を実行すると、ファイル記述子を閉じることができます。
int fd = open(...);
fcntl(fd, F_SETFD, FD_CLOEXEC);
このようなファイル記述子はfork
を存続させますが、exec
ファミリーの関数は存続しません。
私の知る限り、これを行う標準的な方法はありません。
適切に実装しようとしている場合、おそらく最善の方法は、システムコールを追加してファイル記述子をフォークに近いものとしてマークし、_sys_fork
_システムコール(syscall番号)をインターセプトすることです。 2)元の_sys_fork
_を呼び出した後、これらのフラグに基づいて動作します。
新しいシステムコールを追加したくない場合は、_sys_ioctl
_(syscall番号54)をインターセプトし、ファイルの説明をフォークでマークするための新しいコマンドを追加するだけで解決できる場合があります。 。
もちろん、アプリケーションの実行内容を制御できる場合は、フォークで閉じたいすべてのファイル記述子のユーザーレベルのテーブルを維持し、代わりに独自のmyfork
を呼び出す方がよい場合があります。これはフォークし、ユーザーレベルのテーブルを通過して、そのようにマークされたファイル記述子を閉じます。
Linuxカーネルをいじくり回す必要はありません。おそらく、フォークプロセスを制御できない場合にのみ必要なソリューションです(たとえば、サードパーティのライブラリがfork()
呼び出しを実行している場合などです。 )。