tcpポートとudpポート5060の両方でリッスンするSIPサーバーのサンプルアプリケーションがあります。コードのある時点で、system( "pppd file / etc / ppp / myoptions&");を実行します。
この後、netstat -apnを実行すると、ポート5060もpppd用に開かれていることがわかります。これを回避する方法はありますか?Linuxのシステム機能のこの標準的な動作はありますか?
ありがとう、エリソン
はい、デフォルトでは、プロセスをフォークするたびに (フォークしますsystem
)、子はすべての親のファイル記述子を継承します。子がそれらの記述子を必要としない場合は、それらを閉じる必要があります。これをsystem
(または fork+exec を行うその他の方法で) 行う方法は、プロセスの子によって使用されるべきではないすべてのファイル記述子に FD_CLOEXEC フラグを設定することです。これにより、子が他のプログラムを実行するたびに、それらが自動的に閉じられます。
一般に、プログラムが長期間存続し、子と共有されるべきではないファイル記述子の種類を開くときはいつでも(例のリッスンソケットなど)、次のことを行う必要があります。
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
ファイル記述子で。
2016年現在?POSIX.1 のリビジョンでは、SOCK_CLOEXEC
フラグを使用するか、ソケットのタイプに挿入して、ソケットを作成するときにこの動作を自動的に取得できます。
listenfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0);
bind(listenfd, ...
listen(listemfd, ...
system
これにより、同時に実行されている他のスレッドがまたはfork
+exec
呼び出しを行った場合でも、適切に閉じられることが保証されます。幸いなことに、このフラグはしばらくの間 Linux および BSD unix でサポートされていました (残念ながら OSX ではサポートされていません)。
system()
おそらく、関数を完全に避けるべきです。これは本質的に危険であり、Unix 間であっても、改ざんされる可能性があり、むしろ移植できないシェルを呼び出します。
あなたがすべきことはfork()/exec()
ダンスです。それはこのようなものになります
if(!fork()){
//close file descriptors
...
execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL);
perror("exec");
exit(-1);
}
はい、これはが実装されているfork()
Linuxの標準的な動作です。system()
socket()
呼び出しから返された識別子は、有効なファイル記述子です。read()
この値は、write()
、 、ioctl()
、などのファイル指向の関数で使用できますclose()
。
すべてのファイル記述子がソケットであるという逆は正しくありません。で通常のファイルを開き、open()
その記述子をたとえば bind()
またはに渡すことはできませんlisten()
。
子プロセスを呼び出すとsystem()
、親プロセスと同じファイル記述子が継承されます。このようstdout
に(0)、stdin
(1)、stderr
(2)が子プロセスに継承されます。0、1、または 2 のファイル記述子でソケットを開くように設定すると、子プロセスはそのソケットを標準 I/O ファイル記述子の 1 つとして継承します。
子プロセスは、開いたソケットを含め、開いているすべてのファイル記述子を親から継承しています。
他の人が述べているように、これはプログラムが依存する標準的な動作です。
それを防ぐことになると、いくつかのオプションがあります。fork()
デイブが示唆するように、最初に の後にすべてのファイル記述子を閉じます。fcntl
次に、 with を使用しFD_CLOEXEC
て fd ごとに「exec で閉じる」ビットを設定するための POSIX サポートがあります。
最後に、あなたは Linux で実行していると述べたので、何かを開く時点でビットを正しく設定できるように設計された一連の変更があります。当然、これはプラットフォームに依存します。概要はhttp://udrepper.livejournal.com/20407.htmlにあります。
これが意味することは、ソケット作成呼び出しでビット単位または「タイプ」を使用してSOCK_CLOEXEC
フラグを設定できるということです。つまり、カーネル 2.6.27 以降を実行している場合です。
system()
現在のプロセスをコピーし、その上に子を起動します。(現在のプロセスはもうありません。これがおそらく pppd が 5060 を使用する理由です。fork()/exec()
子プロセスを作成して、親プロセスを維持することができます。