2011-08-04 13 views
10

tcpとudpの両方のポートでリッスンしているSIPサーバのサンプルアプリケーションがあります。 コード内のある時点で "pppdファイル/ etc/ppp/myoptions & ");Cのシステムコマンドから開始されたプロセスが親のfdを継承する

これ以降、netstat -apnを実行すると、ポート5060もpppd用に開かれていることがわかります! これを回避する方法はありますか? Linuxのシステム機能の標準的な動作ですか?

おかげで、 Elison

答えて

14

デフォルトでは、プロセスをforkするたびに(system)、すべての親のファイル記述子を継承します。子供がそれらの記述子を必要としない場合は、それらを閉じなければならない(SHOULD)。 system(またはfork + execを実行する他の方法)でこれを行う方法は、あなたのプロセスの子プロセスが使用すべきでないすべてのファイル記述子にFD_CLOEXECフラグを設定することです。これにより、子供が他のプログラムを執行するたびに自動的に閉鎖されます。一般的に

、あなたのプログラムは、(そのようなあなたの例では、ソケットを聞くなど)長期間住むことになるファイルディスクリプタのいずれかの種類を開き、子どもたちと共有すべきでないときはいつでも、あなたは

を行う必要があります
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 

がファイル記述子にあります。


現在、 POSIX.1の改正、あなたはソケットを作成するときに自動的にこの動作を取得するためのソケットのタイプにOR演算SOCK_CLOEXECフラグを使用することができます。それも他のいくつかの場合には、適切に閉じられますguarentees

listenfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0); 
bind(listenfd, ... 
listen(listemfd, ... 

スレッドを同時に実行すると、systemまたはfork + execが呼び出されます。幸運なことに、このフラグは、LinuxやBSD unixではしばらくサポートされています(残念ながら、OSXではサポートされていません)。

+0

はい、私はこれに遭遇しました。 fork()/ exec()を使用するようにコードを変更するまで、このメソッドを使用します。 –

+2

POSIX 2008またはGNUでは、ファイルを開くときに 'O_CLOEXEC'フラグを使用することもできます。これにより、別のスレッド(またはシグナルハンドラ)が 'open'呼び出しと' fcntl'呼び出しの間でfork-and-execする可能性がある場合、別の呼び出しを行う必要がなくなり、* atomic *操作が行われます。 –

3

はおそらく完全にsystem()機能を避ける必要があります。これは本質的に危険です。シェルを呼び出すということです。このシェルは、Un汝ージの間でさえも、持ち運びできず、むしろポータブルではありません。

ダンスはfork()/exec()です。これは、この

if(!fork()){ 
    //close file descriptors 
    ... 

    execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL); 
    perror("exec"); 
    exit(-1); 
} 
+0

ソケット以外のものがあれば、どのfdsを閉じるかを調べる方法についてアドバイスを加えたいと思うかもしれません。おかげさまで – Spudd86

+0

はい、私は間違いなくsystem()をfork()/ exec()に置き換えます。現在私のコードはsystem()を何度も呼びます! WIFEXITEDでコードを返すことができるので、私はシステムを使用しています! Btw、あなたが私に従うことを提案する方法私は/etc/resolv.confのように私のCコードから多くのことを行う必要があるならば、pppdを起動し、pppdをkillし、iptables -Fなどを実行します。私のシステム()。 –

-1

system()コピーし、現在のプロセスのようなものだし、それの上に子を起動します。 (現在のプロセスがそれはpppdは5060を使用していますなぜあなたは子プロセスを作成し、生きている親を保つためにfork()/exec()を試みることができると考えられます。これ以上の存在である。

+1

-1:システムは親プロセスを置換/終了しません。実際、 '' system() '' ''は '' fork() '' '' '' '' exec() '' 'を使います。 –

+0

Heath Hunnicutt:ありがとう、 'system()'の後に2つのプロセスが存在しますか?親子? – hari

+0

'' '' system() '' 'の後に、親プロセスと子プロセスの2つのプロセスがありますが、呼び出しはまだ戻っていません。 '' 'system()' 'の実装は、子のPIDに対して' 'waitpid''を呼び出します。 '' 'pppd'''の場合、プロセスは自立しています(' '' waitpid'''を伴わない '' '' '' exec''')ので、一時的に* 3 *プロセス、2つのppd、1つの親。 '' 'system''が復帰する時までに、C内の戻り値は生成されたプログラムによって' '' exit''に与えられた値を含んでいるので、子は終了しました。 '' system''が返ったら、子は終了していなければなりません。 –

1

はい、これはsystem()が実装されているから、Linuxでfork()の標準的な動作であり、 。

socket()呼び出しから返された識別子が有効なファイルディスクリプタである。この値は、このようなread()write()ioctl()などのファイル指向の機能で使用可能で、かつclose()

逆の、すべてのファイル記述子であることソケット、私本当ではない。 open()で通常のファイルを開き、そのディスクリプタをたとえばbind()またはlisten()に渡すことはできません。

system()を呼び出すと、子プロセスは親と同じファイル記述子を継承します。これは、stdout(0)、stdin(1)、およびstderr(2)が子プロセスによって継承される方法です。ファイル記述子が0,1または2のソケットを開くと、子プロセスはそのソケットを標準I/Oファイル記述子の1つとして継承します。

あなたの子プロセスは、あなたが開いたソケットを含め、親から開いているすべてのファイル記述子を継承しています。

+0

udpポートとtcpポートの両方の2つのネットワークインターフェイスでSIPサーバーを開きます。したがって、system()コールの前に、netstatはeth60の場合は5060 - 2 tcp、eth1の場合は2つのudpの4つのエントリを表示します。システム(pppd ...)の呼び出しの後、netstatはpppd()のエントリを6つ与えます。 pppdはeth0に対して2つのソケットを継承します。これは、一度pppdリンクが確立されると、私のメインアプリケーションのeth1インターフェース上のSIPサーバーソケットを閉じるためです。これは2つだけが継承される理由を説明します。私は2秒間隔でシェルスクリプトからnetstatを呼び出すので、pppdがすべての4ソケットを継承したときに私が見逃しているかもしれません。 –

1

他の人が述べたように、これはプログラムが依存する標準的な動作です。

これを防ぐには、いくつかの選択肢があります。まず、Daveが示唆しているように、fork()の後にすべてのファイル記述子を閉じます。第二に、fcntlFD_CLOEXECを使用して、fd単位で 'close on exec'ビットを設定するPOSIXサポートがあります。

最後に、あなたがLinux上で動作していると言われているので、オープニングの時点でビットを正しく設定できるように一連の変更があります。もちろん、これはプラットフォームに依存します。 http://udrepper.livejournal.com/20407.html

つまり、SOCK_CLOEXECフラグを設定するために、ソケット作成呼び出しでビット単位または 'タイプ'を使用できます。カーネル2.6を実行している場合27またはそれ以降。

+0

私は2.6.30を実行していますので、FD_CLOEXECでfcntlを使用します。 –

関連する問題