10

Linux 上の C プログラムで問題が発生しました。

プロセスがフォークされると、子プロセスは、開いているファイル記述子など、親からいくつかのものを継承することを知っています。

問題は、新しい接続を受け入れて記述子を共有メモリに配置するマスター プロセスを使用して、マルチプロセス サーバー アプリケーションを作成していることです。

子プロセスが共有メモリからこれらの記述子の 1 つを読み取ろうとするとselect()EBADFエラーが発生しました。

子プロセスは、フォークされたに親プロセスによって作成されたソケット (または一般的なファイル記述子) をどのように読み取って使用できますか?

4

2 に答える 2

16

fork を呼び出すと、子プロセスは開いているすべてのファイル記述子のコピーを継承します。これを行う一般的な方法は、親プロセスがリッスン ソケットを開き、accept を呼び出して接続が到着するまでブロックし、接続の受信後に fork を呼び出すことです。次に、親はファイル記述子のコピーを閉じますが、新しい子プロセスはファイル記述子を使用し続け、必要な処理を実行できます。子が完了すると、ソケットも閉じます。2 つのことを覚えておくことが重要です: 1. ファイル記述子/ソケットはオペレーティング システムのリソースであり、フォークの後、親と子はそれぞれそのリソースへのハンドルを持ちます。これは参照カウント スマート ポインターのようなものです。ここで詳しく説明します. 2 つ目は、fork を呼び出す前に開かれたファイル記述子のみが共有されることです。これは、fork の前に存在していたファイル記述子などのリソースを共有している場合でも、fork 後は親と子が完全に別のプロセスになるためです。親がワーカー プロセスに作業を渡すモデルを使用している場合は、スレッドとスレッド プールの使用を検討することをお勧めします。

ところで、Unix Network ProgrammingのWeb サイトから、サーバーとクライアントの優れたサンプルの割り当てをダウンロードできます。

于 2013-01-20T20:44:48.910 に答える
13

共有メモリを介して、あるプロセスから別のプロセスにソケット (またはその他のファイル記述子) を送信することはできません。ファイル記述子は小さな整数です。この整数を共有メモリに配置して別のプロセスからアクセスしても、同じ整数が自動的に他のプロセスの観点から有効なファイル記述子になるわけではありません。

あるプロセスから別のプロセスにファイル記述子を送信する正しい方法は、2 つのプロセス間の既存のソケット通信チャネルSCM_RIGHTSを介して補助データとして送信することです。sendmsg()

まず、 のsocketpair()前に とのコミュニケーション チャネルを作成しますfork()。次に、親でソケット ペアの一方の端を閉じ、子でもう一方の端を閉じます。sendmsg()このソケットの一方の端で親から受信しrecvmsg()、もう一方の端を使用して子で受信できるようになりました。

でメッセージを送信すると、SCM_RIGHTS次のようになります。

struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(int))];
char dummy[2];    

memset(&m, 0, sizeof(m));
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = &buf;
memset(m.msg_control, 0, m.msg_controllen);
cm = CMSG_FIRSTHDR(&m);
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
cm->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cm)) = your_file_descriptor_to_send;
m.msg_iov = &iov;
m.msg_iovlen = 1;
iov.iov_base = dummy;
iov.iov_len = 1;
dummy[0] = 0;   /* doesn't matter what data we send */
sendmsg(fd, &m, 0);

でメッセージを受信するとSCM_RIGHTS、次のようになります。

struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
struct dummy[100];
char buf[CMSG_SPACE(sizeof(int))];
ssize_t readlen;
int *fdlist;

iov.iov_base = dummy;
iov.iov_len = sizeof(dummy);
memset(&m, 0, sizeof(m));
m.msg_iov = &iov;
m.msg_iovlen = 1;
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = buf;
readlen = recvmsg(fd, &m, 0);
/* Do your error handling here in case recvmsg fails */
received_file_descriptor = -1; /* Default: none was received */
for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) {
    if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) {
        nfds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int);
        fdlist = (int *)CMSG_DATA(cm);
        received_file_descriptor = *fdlist;
        break;
    }
}
于 2013-01-20T18:59:59.333 に答える