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

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

此时,我最大的问题是让我的应用程序处理可执行文件。我在搜索跨平台流程管理库时发现Boost.Process,并决定尝试一下。正如some evidence所暗示的那样,似乎v0.5是它的最新工作版本,并且可以推断出有很多人在使用它。

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

文件: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上,尽管btsync进程没有终止,但它在启动进程后立即完成了执行,就好像根本没有wait_for_exit()一样。

为了查看是否与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()将引发异常:



这与Windows上的行为不同,在Windows上仅说“确定”并以状态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 forked to background 。而已。没什么。如果愿意,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

没问题!

更好的伪同步

这应该仍然是可移植的,并且在被杀死/终止/中断时表现得更好:
#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

可以使用“official” btsync和“fake” 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

10-08 11:05