c++ - 进程管理代码在 Linux 和 Windows 上的行为不同 - 为什么?

标签 c++ linux boost process cross-platform

(上下文) 我正在开发一个跨平台(Windows 和 Linux)应用程序,用于在计算机之间分发文件,基于 BitTorrent Sync 。我已经用 C# 完成了它,现在正在移植到 C++ 作为练习。

BTSync 可以在 API mode 中启动,为此,必须启动“btsync”可执行文件,并将配置文件的名称和位置作为参数传递。

此时,我最大的问题是让我的应用程序处理可执行文件。我来发现Boost.Process在寻找跨平台流程管理库时,决定尝试一下。看来 v0.5 是它的最新工作版本,如 some evidence建议,并且可以推断有很多人在使用它。

我按如下方式实现了该库(仅相关代码):

文件:test.hpp

namespace testingBoostProcess
{
    class Test
    {
        void StartSyncing();
    };
}

文件:Test.cpp

#include <string>
#include <vector>
#include <iostream>
#include <boost/process.hpp>
#include <boost/process/mitigate.hpp>
#include "test.hpp"

using namespace std;
using namespace testingBoostProcess;
namespace bpr = ::boost::process;

#ifdef _WIN32
const vector<wstring> EXE_NAME_ARGS = { L"btsync.exe", L"/config", L"conf.json" };
#else
const vector<string> EXE_NAME_ARGS = { "btsync", "--config", "conf.json" };
#endif

void Test::StartSyncing()
{
    cout << "Starting Server...";
    try
    {
        bpr::child exeServer = bpr::execute(bpr::initializers::set_args(EXE_NAME_ARGS),
            bpr::initializers::throw_on_error(), bpr::initializers::inherit_env());

        auto exitStatus = bpr::wait_for_exit(exeServer);    //  type will be either DWORD or int
        int exitCode = BOOST_PROCESS_EXITSTATUS(exitStatus);
        cout << " ok" << "\tstatus: " << exitCode << "\n";
    }
    catch (const exception& excStartExeServer)
    {
        cout << "\n" << "Error: " << excStartExeServer.what() << "\n";
    }
}


(问题) 在 Windows 上,上述代码将启动 btsync 并等待(阻止)直到进程终止(通过使用任务管理器或通过 API 的 shutdown 方法),就像所期望的那样。
但在 Linux 上,它在启动进程后立即完成执行,就好像 wait_for_exit() 根本不存在一样,尽管 btsync 进程并未终止。 p>

为了看看这是否与 btsync 可执行文件本身有关,我用这个简单的程序替换了它:

文件:Fake-Btsync.cpp

#include <cstdio>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define SLEEP Sleep(20000)
#include <Windows.h>
#else
#include <unistd.h>
#define SLEEP sleep(20)
#endif
using namespace std;

int main(int argc, char* argv[])
{
    for (int i = 0; i < argc; i++)
    {
        printf(argv[i]);
        printf("\n");
    }
    SLEEP;
    return 0;
}


当与此程序一起使用时,我的应用程序可以按预期工作,而不是从官方网站下载原始的btsync。它将阻塞 20 秒,然后退出。

问题:所描述的行为的原因是什么?我唯一能想到的是 btsync 在 Linux 上会自行重新启动。但如何确认呢?或者还能是什么?

更新:我所需要做的就是了解 forking 是什么。是以及它是如何工作的,正如 sehe 的回答所指出的(谢谢!)。


问题 2:如果我在主应用程序被阻止时使用系统监视器向子进程“Fake-Btsync”发送 End 命令, wait_for_exit() 将抛出异常:

waitpid(2) failed: No child processes

这与 Windows 上的行为不同,在 Windows 上它只是简单地说“ok”并以状态 0 终止。

更新 2: sehe 的回答很好,但没有以我真正理解的方式完全解决问题 2。我将就此写一个新问题并在此处发布链接。

最佳答案

问题在于你对 btsync 的假设。让我们开始吧:

./btsync 
By using this application, you agree to our Privacy Policy, Terms of Use and End User License Agreement.
http://www.bittorrent.com/legal/privacy
http://www.bittorrent.com/legal/terms-of-use
http://www.bittorrent.com/legal/eula

BitTorrent Sync forked to background. pid = 24325. default port = 8888

所以,这就是整个故事:BitTorrent Sync fork 到后台。而已。一点也没有少。如果您愿意,btsync --help 会告诉您传递 --nodaemon

测试进程终止

让我们通过--nodaemon使用测试程序运行btsync。在一个单独的子 shell 中,让我们在 5 秒后终止子 btsync 进程:

sehe@desktop:/tmp$ (./test; echo exit code $?) & (sleep 5; killall btsync)& time wait
[1] 24553
[2] 24554
By using this application, you agree to our Privacy Policy, Terms of Use and End User License Agreement.
http://www.bittorrent.com/legal/privacy
http://www.bittorrent.com/legal/terms-of-use
http://www.bittorrent.com/legal/eula

[20141029 10:51:16.344] total physical memory 536870912 max disk cache 2097152
[20141029 10:51:16.344] Using IP address 192.168.2.136
[20141029 10:51:16.346] Loading config file version 1.4.93
[20141029 10:51:17.389] UPnP: Device error "http://192.168.2.1:49000/l2tpv3.xml": (-2) 
[20141029 10:51:17.407] UPnP: ERROR mapping TCP port 43564 -> 192.168.2.136:43564. Deleting mapping and trying again: (403) Unknown result code (UPnP protocol violation?)
[20141029 10:51:17.415] UPnP: ERROR removing TCP port 43564: (403) Unknown result code (UPnP protocol violation?)
[20141029 10:51:17.423] UPnP: ERROR mapping TCP port 43564 -> 192.168.2.136:43564: (403) Unknown result code (UPnP protocol violation?)
[20141029 10:51:21.428] Received shutdown request via signal 15
[20141029 10:51:21.428] Shutdown. Saving config sync.dat
Starting Server... ok   status: 0
exit code 0
[1]-  Done                    ( ./test; echo exit code $? )
[2]+  Done                    ( sleep 5; killall btsync )

real    0m6.093s
user    0m0.003s
sys 0m0.026s

没问题!

更好的假 Btsync

这应该仍然是可移植的,并且在被杀死/终止/中断时表现得更好:

#include <boost/asio/signal_set.hpp>
#include <boost/asio.hpp>
#include <iostream>

int main(int argc, char* argv[])
{
    boost::asio::io_service is;
    boost::asio::signal_set ss(is);
    boost::asio::deadline_timer timer(is, boost::posix_time::seconds(20));
    ss.add(SIGINT);
    ss.add(SIGTERM);

    auto stop = [&]{ 
        ss.cancel();    // one of these will be redundant
        timer.cancel();
    };

    ss.async_wait([=](boost::system::error_code ec, int sig){ 
            std::cout << "Signal received: " << sig << " (ec: '" << ec.message() << "')\n"; 
            stop();
        });
    timer.async_wait([&](boost::system::error_code ec){
            std::cout << "Timer: '" << ec.message() << "'\n";
            stop();
        });

    std::copy(argv, argv+argc, std::ostream_iterator<std::string>(std::cout, "\n"));
    is.run();

    return 0;
}

可以测试一下是否表现良好

(./btsync --nodaemon; echo exit code $?) & (sleep 5; killall btsync)& time wait

可以使用“官方”btsync 和“假”btsync 运行相同的测试。我的 Linux 机器上的输出:

sehe@desktop:/tmp$ (./btsync --nodaemon; echo exit code $?) & (sleep 5; killall btsync)& time wait
[1] 24654
[2] 24655
./btsync
--nodaemon
Signal received: 15 (ec: 'Success')
Timer: 'Operation canceled'
exit code 0
[1]-  Done                    ( ./btsync --nodaemon; echo exit code $? )
[2]+  Done                    ( sleep 5; killall btsync )

real    0m5.014s
user    0m0.001s
sys 0m0.014s

关于c++ - 进程管理代码在 Linux 和 Windows 上的行为不同 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26624871/

相关文章:

c++ - xvalue 上的下标表达式的值类别

c++ - 是否允许在 Base 的实例上编写 Derived 的实例?

c - Linux : Glib was not found

linux - 如何将超链接读入变量

c++ - 使用 boost 序列化将 C++ 中的对象序列化为二进制格式以便通过套接字使用是否安全?

c++ - 移动而不是复制 boost::dynamic_bitset BlockInputIterator 构造函数?

c++ - Alexandrescu 的想法与现代 C++ 相关吗?

c++ - 编译器错误 : Template class assignment no match

c - 是否有一种机制可以操纵像 prctl() 这样的进程,但用于 forked/exec*()d 进程?

c++ - boost:asio 线程池实现,用于偶尔同步的任务