通过system()调用启动另一个程序会阻塞套接字 [英] Starting another program via system() call blocks the socket

查看:129
本文介绍了通过system()调用启动另一个程序会阻塞套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在google和StackOverflow中搜索了与此类似的内容,但最接近的是C代码,情况不一样...

I searched google and StackOverflow for anything similar to this but the closest ones were C code and the situation wasn't the same...

我有一个程序,该程序通过cstdlib的system()调用脚本来启动另一个程序,并且一切正常,问题是当我必须测试新代码时,因此我停止执行(Crtl + C并杀死-9 pid产生)相同的错误),编译新的可执行文件,然后尝试再次运行它,这是在我收到该套接字已被使用的消息时.

I have a program which starts another one via cstdlib's system() call to a script and everything works fine, the problem is when I have to test new code, so I stop execution (Crtl+C and kill -9 pid produce the same error), compile the new executable and try to run it again, that's when I get the message that the socket is already in use.

我的程序使用一个接受器并等待连接(它一次处理一个).它可以通过一个脚本检索/发送文件并启动/停止另一个程序.如果我只是检索/发送文件,则套接字不会被锁定,但是如果执行了任何system()调用,则套接字将被锁定,并且在关闭程序后我将无法运行该程序.

My program uses an acceptor and waits for connection (it handles one at a time). It can retrieve/send files and start/stop another program via one script. If I just retrieve/send files, the socket isn't locked, but if any of the system() calls is executed, then the socket gets locked and I can't run the program after I shut it down.

我真的认为这与实际的asio部分无关,因为它可以正常工作,但这是我定义端点的方式:

I really don't think this has something to do with the actual asio part, since it works properly, but this is how I defined my endpoint:

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

ba是boost :: asio的缩写. 还有ssl:

Where ba is short for boost::asio. And the 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;

无论如何,为了更清楚一点,这是两种情况:

Anyway, just to be clearer, these are the 2 situations:

首先:(确定)

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

第二个:(错误)

#./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中的脚本),并且得到了相同的结果.

I thought maybe the other program was doing something wrong, so I tried using my system call to start another program (Apache, via it's script in init.d) and I got the same result.

因此,我在为什么会发生"和我如何解决此问题"方面有些迷茫...

So I'm a little lost here in "why is this happening" and "how I can solve this"...

system()似乎与它有关,但是我不知道为什么原因,因为调用返回并且套接字与它无关. 甚至从子进程(通过fork())使用execv()调用相同的命令也会产生相同的错误...

system() seems to have something to do with it, but I have no clue why since the call returns and the socket has nothing to do with it. Even invoking the same command with execv() from a child process (via fork()) produces the same error...

仍然没有尝试过popen(),但是我敢肯定我会到达相同的地方.

Still haven't tried popen(), but I'm sure I'll reach the same place.

刚刚意识到system()实际上是一个exec之后的fork(),因此没有必要指出...

Just realised that system() is actually a fork() followed by an exec, so there was no point in pointing that out...

添加更多详细信息

在Tanner Sansbury回答之后,我更改了代码,但结果仍然相同...

Following Tanner Sansbury answer, I changed my code, but still the same result...

在操作上,我只读取了该操作并调用了相关的函数.

On the operations I just read the operation and call the function related.

然后我就依靠我的功能:

Then I fall on my function:

//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;

existingBinary()函数:

The existBinary() function:

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;
}

请注意,当孩子退出时,我放了一张纸条打印出来,但是什么也没显示...:(

Notice that I put a cout to print when the child exited, but it shows nothing... :(

我将尝试将接受者变成班级成员,并将其关闭给孩子.

I'll try moving the acceptor to be a class member and closing it on the child.

同样,当我重新启动/停止其他程序时,套接字已释放...

Again, the socket IS FREED when I restart/stop THE OTHER PROGRAM...

推荐答案

简而言之,根据观察到的行为,问题的根源很可能是fork()的行为所致,因为子进程将继承一个副本接受者的打开文件描述符.从孩子内部关闭接受者应该可以解决问题.

In short, based on the observed behaviors, the source of the problem is likely resulting from fork()'s behavior, as the child process will inherit a copy of the acceptor's open file descriptor. Closing the acceptor from within the child should resolve the problem.

发生 fork() 时,子进程将继承父进程集的副本.打开文件描述符.以下是fork文档的相关摘录:

When fork() occurs, the child process inherits copies of the parent's set of open file descriptors. Here is the relevant excerpt from the fork documentation:

子级继承父级打开文件描述符集的副本.子级中的每个文件描述符都引用与父级中相应的文件描述符相同的打开文件描述.这意味着这两个描述符共享打开文件状态标志,当前文件偏移和信号驱动的I/O属性.

The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description as the corresponding file descriptor in the parent. This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O attributes .

但是,Boost.Asio具有内部文件描述符,这些文件描述符也将被复制.需要为fork()准备io_service,并在fork()发生后通知. Boost.Asio文档是正确的 fork 用法.根据文档,在fork:

However, Boost.Asio has internal file descriptors that will also be duplicated. The io_service needs to be prepared for fork(), and notified after fork() occurs. Boost.Asio document's the proper fork usage. Per the documentation, it is the program's responsibility to manage any file descriptors accessible via Boost.Asio's public API during a 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的每个连接的进程,以示例为例,该进程是从完成处理程序中派生出来的.

See Boost.Asio's process per connection for an example where the process forks from within a completion handler.

这篇关于通过system()调用启动另一个程序会阻塞套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆