是否有可能在C中使用系统api启动一个kill命令?如果没有其他选择的话

Is it possible a kill a command launched using system api in C? If not any alternatives?

本文关键字:命令 kill 一个 如果没有 选择 其他 有可能 启动 api 系统 是否      更新时间:2023-10-16

我正在使用系统api启动命令(我可以使用这个api与C/c++ )。我传递的命令可能会挂起,因此我希望在超时后杀死它。

目前我使用的是:

system("COMMAND");

我想这样使用它:

使用系统独立的API运行命令(我不想使用CreateProcess,因为它仅适用于Windows)如果在'X'分钟后未退出,则杀死进程。

由于system()是特定于平台的调用,因此不可能有独立于平台的方法来解决您的问题。然而,system()是一个POSIX调用,所以如果它在任何给定的平台上都得到支持,那么POSIX API的其余部分也应该得到支持。因此,解决问题的一种方法是使用fork()kill()

system()调用一个shell有一个复杂的地方,它可能会产生其他进程,我假设您想要杀死所有的进程,所以一种方法是使用一个进程组。基本思想是使用fork()创建另一个进程,将其放在自己的进程组中,如果它在一定时间后没有退出,则杀死该进程组。

一个简单的例子——程序分叉;子进程将自己的进程组设置为与其进程ID相同,并使用system()生成一个无限循环。父进程等待10秒,然后使用子进程PID的负值杀死进程组。这将终止分支进程和该进程的所有子进程(除非它们更改了它们的进程组)。

由于父进程在不同的组中,kill()对它没有影响。

#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
int main() {
    pid_t pid;
    pid = fork();
    if(pid == 0) { // child process
        setpgid(getpid(), getpid());
        system("while true ; do echo xx ; sleep 5; done");
    } else {   // parent process
        sleep(10);
        printf("Sleep returnedn");
        kill(-pid, SIGKILL);
        printf("killed process group %dn", pid);
    }
    exit(0);
}

没有标准的、跨平台的系统API。提示是它们是系统api !我们实际上很"幸运",我们得到了system,但我们没有得到其他任何东西。

你可以试着找一些第三方抽象

检查下面基于c++线程的linux尝试。(未测试)

#include <iostream>
#include <string>
#include <thread>
#include <stdio.h>
using namespace std;
// execute system command and get output
// http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c
std::string exec(const char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
    pclose(pipe);
    return result;
}
void system_task(string& cmd){
    exec(cmd.c_str());
}
int main(){
    // system commad that takes time
    string command = "find /";
    // run the command in a separate thread
    std::thread t1(system_task, std::ref(command));
    // gives some time for the system task
    std::this_thread::sleep_for(chrono::milliseconds(200));
    // get the process id of the system task
    string query_command = "pgrep -u $LOGNAME " + command;
    string process_id = exec(query_command.c_str());
    // kill system task
    cout << "killing process " << process_id << "..." << endl;
    string kill_command = "kill " + process_id;
    exec(kill_command.c_str());
    if (t1.joinable())
        t1.join();
    cout << "continue work on main thread" << endl;
    return 0;
}

我有一个类似的问题,在Qt/QML开发中:我想启动一个bash命令,同时继续在Qt循环中处理事件,如果bash命令花费的时间太长,就杀死它。

我想出了下面的课程,我在这里分享(见下文),希望它可能对有类似问题的人有所帮助。

我没有调用'kill'命令,而是调用开发人员提供的cleanupCommand。例如:如果我要调用myscript.sh并想要检查它是否持续运行不超过10秒,我将以以下方式调用它:

SystemWithTimeout systemWithTimeout("myScript.sh", 10, "killall myScript.sh");
systemWithTimeout.start();
代码:

class SystemWithTimeout {
private:
    bool m_childFinished = false ;
    QString m_childCommand ;
    int m_seconds ;
    QString m_cleanupCmd ;
    int m_period;
    void startChild(void) {
        int rc = system(m_childCommand.toUtf8().data());
        if (rc != 0) SYSLOG(LOG_NOTICE, "Error SystemWithTimeout startChild: system returned %d", rc);
        m_childFinished = true ;
    }
public:
    SystemWithTimeout(QString cmd, int seconds, QString cleanupCmd)
        : m_childFinished {false}, m_childCommand {cmd}, m_seconds {seconds}, m_cleanupCmd {cleanupCmd}
        { m_period = 200; }
    void setPeriod(int period) {m_period = period;}
    void start(void) ;
};
void SystemWithTimeout::start(void)
{
    m_childFinished = false ; // re-arm the boolean for 2nd and later calls to 'start'
    qDebug()<<"systemWithTimeout"<<m_childCommand<<m_seconds;
    
    QTime dieTime= QTime::currentTime().addSecs(m_seconds);
    std::thread child(&SystemWithTimeout::startChild, this);
    child.detach();
    while (!m_childFinished && QTime::currentTime() < dieTime)
    {
        QTime then = QTime::currentTime();
        QCoreApplication::processEvents(QEventLoop::AllEvents, m_period); // Process events during up to m_period ms (default: 200ms)
        QTime now = QTime::currentTime();
        int waitTime = m_period-(then.msecsTo(now)) ;
        QThread::msleep(waitTime); // wait for the remaning of the 200 ms before looping again.
    }
    if (!m_childFinished)
    {
        SYSLOG(LOG_NOTICE, "Killing command <%s> after timeout reached (%d seconds)", m_childCommand.toUtf8().data(), m_seconds);
        int rc = system(m_cleanupCmd.toUtf8().data());
        if (rc != 0) SYSLOG(LOG_NOTICE, "Error SystemWithTimeout 164: system returned %d", rc);
        m_childFinished = true ;
    }
    
}

我不知道在C或c++语言中有任何可移植的方法来做到这一点。当你要求替代时,我知道在其他语言中是可能的。例如,在Python中,可以使用subprocess模块。

import subprocess 
cmd = subprocess.Popen("COMMAND", shell = True)
你可以测试COMMAND是否以 结尾
if cmd.poll() is not None:
    # cmd has finished

你可以用:

cmd.terminate()

即使你更喜欢使用C语言,你也应该阅读subprocess模块的文档,因为它解释了它在Windows系统上使用CreateProcess和Posix系统上使用os.execvp来启动命令,在Windows系统上使用TerminateProcess和Posix系统上使用SIG_TERM来停止命令。