0

これに似たものをGoogleとStackOverflowで検索しましたが、最も近いものはCコードで、状況は同じではありませんでした...

スクリプトへの cstdlib の system() 呼び出しを介して別のプログラムを開始するプログラムがあり、すべて正常に動作します。問題は、新しいコードをテストする必要があるときです。そのため、実行を停止します (Crtl+C および kill -9 pid で同じエラーが発生します)。 )、新しい実行可能ファイルをコンパイルして再度実行しようとすると、ソケットが既に使用されているというメッセージが表示されます。

私のプログラムはアクセプターを使用し、接続を待ちます (一度に 1 つずつ処理します)。ファイルの取得/送信と、別のプログラムの開始/停止を 1 つのスクリプトで実行できます。ファイルを取得/送信するだけの場合、ソケットはロックされませんが、system() 呼び出しのいずれかが実行されると、ソケットがロックされ、シャットダウンした後にプログラムを実行できなくなります。

適切に動作するため、これが実際の asio 部分と関係があるとは本当に思いませんが、これがエンドポイントの定義方法です。

ba::ip::tcp::endpoint endpoint(ba::ip::address_v4::any(), listen_port);

ba は boost::asio の略です。そしてssl:

ba::ip::tcp::acceptor acceptor_(*io,endpoint);
ba::ssl::context context_(*io, ba::ssl::context::sslv23);

context_.set_options( ba::ssl::context::default_workarounds |
                      ba::ssl::context::no_sslv2 |
                      ba::ssl::context::single_dh_use);
//Rest of context config

//Some more code

stream.reset( new ssl_stream(*io,context_) );

boost::system::error_code ec;
boost::system::error_code no_error; // default error_code = no error

while(true)
{
    ec = no_error;
    stream.reset( new ssl_stream(*io,context_) );
    acceptor_.accept(stream->lowest_layer(), endpoint,ec);
    if( !ec )
    {
        stream->handshake(ba::ssl::stream_base::server,ec);

        if( !ec )
        {
            operations();
        }
        //rest of the code...
}//while(true) end
acceptor_.close();
if(pid == 0)
{
    std::cout << "Child exiting!" << std::endl;
}

ストリームの場所:

boost::shared_ptr< boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > stream;

とにかく、明確にするために、これらは2つの状況です:

最初: (OK)

# ./program
(Then, I only retrieve/send files)
# kill -9 program_pid ( or Crtl+C)
# g++ .... -o program
# ./program
(Program starts normally)

2 番目: (エラー)

#./program
(Use system() to start other program 1+ times)
# kill -9 program_pid (or Crtl+C)
# g++ .... -o program
# ./program
(Program fails to start, since socket "is already being used")

面白い事実:

  1. プログラムで開始した他のプログラムを (ターミナル経由で) 再起動/停止すると、ソケットが解放されます。
  2. fork() -> execv() 同じコマンドでも、ソケットが「ロック」されます。
  3. 開始されたプログラムの親 PID は 1 であり、私のプログラムの PID ではありません...

他のプログラムが何か間違ったことをしているのかもしれないと思ったので、システムコールを使用して別のプログラム(Apache、init.dのスクリプトを介して)を開始しようとしましたが、同じ結果が得られました。

ここで、「なぜこうなったのか」「どうすれば解決できるのか」に少し迷います...

system() はそれと関係があるようですが、呼び出しが返され、ソケットはそれとは関係がないため、理由はわかりません。子プロセスから ( fork() を介して) execv() を使用して同じコマンドを呼び出しても、同じエラーが発生します...

popen() はまだ試していませんが、同じ場所にたどり着くと確信しています。

編集: system() は実際には fork() の後に exec が続くことに気付いたので、それを指摘しても意味がありませんでした...

EDIT2:詳細を追加

Tanner Sansbury の回答に従って、コードを変更しましたが、結果は同じです...

操作については、操作を読み取り、関連する関数を呼び出すだけです。

それから私は自分の機能に落ちます:

//Function body:

if (this->existBinary())
{
    io->notify_fork(boost::asio::io_service::fork_prepare);

    pid = fork();

    if( pid == 0 )
    {
        io->notify_fork(boost::asio::io_service::fork_child);

        char binChar[80];

        for(unsigned int i = 0; i < bin.size(); ++i)
        {
            binChar[i] = bin[i];
        }

        binChar[bin.size()] = '\0';

        char* bin_args[] = { binChar, "start" , NULL };
        execv( bin_args[0], bin_args );
    }
    else
    {
        io->notify_fork(boost::asio::io_service::fork_parent);

        if( pid == -1)
        {
            std::cout << "Fork error" << std::endl;
        }
        else
        {
            // running in parent, wait exec to complete
            // and return its exit status.
            int status;
            waitpid( pid, &status, 0 );
            printf("Child %d exited with status %d\n", pid, status );
        }
    }

    // returning true just for testing
    return true;
}

return false;

existBinary() 関数:

bool my_class::existBinary(const std::string& filename) const
{
    std::ifstream file(filename.c_str(),std::ios_base::in | std::ios_base::binary);
    bool file_status = false;

    if(file.is_open())
    {
        file_status = true;
        file.close();
    }

    return file_status;
}

子が終了したときに cout を印刷することに注意してください。しかし、何も表示されません... :(

アクセプターをクラス メンバーに移動し、子で閉じてみます。

繰り返しますが、他のプログラムを再起動/停止すると、ソケットが解放されます...

4

1 に答える 1

2

つまり、観察された動作に基づいてfork()、子プロセスがアクセプターの開いているファイル記述子のコピーを継承するため、問題の原因は の動作に起因する可能性があります。子内からアクセプターを閉じると、問題が解決するはずです。


発生するfork()と、子プロセスは親の開いているファイル記述子のセットのコピーを継承します。forkドキュメントからの関連する抜粋は次のとおりです。

子は、開いているファイル記述子の親のセットのコピーを継承します。子の各ファイル記述子は、親の対応するファイル記述子と同じオープン ファイル記述を参照します。これは、2 つの記述子がオープン ファイル ステータス フラグ、現在のファイル オフセット、およびシグナル駆動型 I/O 属性を共有することを意味します。

ただし、Boost.Asio には複製される内部ファイル記述子があります。に備え、発生後に通知するio_service必要があります。Boost.Asio ドキュメントは適切なフォークの使用法です。ドキュメントによると、Boost.Asio のパブリック API を介してアクセス可能なファイル記述子を管理するのは、プログラムの責任です。fork()fork()fork

// Inform the io_service that we are about to fork. The io_service cleans
// up any internal resources, such as threads, that may interfere with
// forking.
io_service_.notify_fork(boost::asio::io_service::fork_prepare);

if (0 == fork())
{
  // Inform the io_service that the fork is finished and that this is the
  // child process. The io_service uses this opportunity to create any
  // internal file descriptors that must be private to the new process.
  io_service_.notify_fork(boost::asio::io_service::fork_child);

  // The child won't be accepting new connections, so we can close the
  // acceptor. It remains open in the parent.
  acceptor_.close();
  system(...);
  ...
}
else
{
  // Inform the io_service that the fork is finished (or failed) and that
  // this is the parent process. The io_service uses this opportunity to
  // recreate any internal resources that were cleaned up during
  // preparation for the fork.
  io_service_.notify_fork(boost::asio::io_service::fork_parent);
  ...
}

プロセスが完了ハンドラ内から分岐する例については、接続ごとの Boost.Asio のプロセスを参照してください。

于 2013-07-09T14:40:38.097 に答える