Getting the PID from popen

Getting the PID from popen

本文关键字:popen from PID the Getting      更新时间:2023-10-16

我有一个程序,它使用popen()打开并读取shell命令的输出。问题是,据我所知,没有简单的方法可以获得正在运行的进程的PID,因此,如果它卡住了,你就无法杀死它。所以问题是,如何从用popen打开的进程中检索PID?

我提出的解决方案(以及普遍共识)是创建一个新的popen函数,允许我检索PID。由于我在SO上找不到一个简单的例子,我想发布我的实现,希望它能帮助其他人。欢迎反馈和替代解决方案。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <string>
#include <sstream>
using namespace std;
#define READ   0
#define WRITE  1
FILE * popen2(string command, string type, int & pid)
{
    pid_t child_pid;
    int fd[2];
    pipe(fd);
    if((child_pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }
    /* child process */
    if (child_pid == 0)
    {
        if (type == "r")
        {
            close(fd[READ]);    //Close the READ end of the pipe since the child's fd is write-only
            dup2(fd[WRITE], 1); //Redirect stdout to pipe
        }
        else
        {
            close(fd[WRITE]);    //Close the WRITE end of the pipe since the child's fd is read-only
            dup2(fd[READ], 0);   //Redirect stdin to pipe
        }
        setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
        execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);
        exit(0);
    }
    else
    {
        if (type == "r")
        {
            close(fd[WRITE]); //Close the WRITE end of the pipe since parent's fd is read-only
        }
        else
        {
            close(fd[READ]); //Close the READ end of the pipe since parent's fd is write-only
        }
    }
    pid = child_pid;
    if (type == "r")
    {
        return fdopen(fd[READ], "r");
    }
    return fdopen(fd[WRITE], "w");
}
int pclose2(FILE * fp, pid_t pid)
{
    int stat;
    fclose(fp);
    while (waitpid(pid, &stat, 0) == -1)
    {
        if (errno != EINTR)
        {
            stat = -1;
            break;
        }
    }
    return stat;
}
int main()
{
    int pid;
    string command = "ping 8.8.8.8"; 
    FILE * fp = popen2(command, "r", pid);
    char command_out[100] = {0};
    stringstream output;
    //Using read() so that I have the option of using select() if I want non-blocking flow
    while (read(fileno(fp), command_out, sizeof(command_out)-1) != 0)
    {
        output << string(command_out);
        kill(-pid, 9);
        memset(&command_out, 0, sizeof(command_out));
    }
    string token;
    while (getline(output, token, 'n'))
        printf("OUT: %sn", token.c_str());
    pclose2(fp, pid);
    return 0;
}

澄清

我试图使用@Gillespie的答案定义的函数,但发现C/C++程序中的pid与终端命令pgrep返回的函数不同,从ps -aux | grep myNameProc的输出来看,C程序的进程似乎再次分叉。

我认为是因为execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);实际上相当于/bin/sh cmd string。因此,基本上,C(或C++)程序的子进程正在创建一个执行/bin/sh yourRealProcess的新进程,其中yourRealProcess是在command字符串中指定的进程。

我解决了以下问题:execl(command.c_str(), command.c_str(), (char*)NULL);。然而,正如@Gillespie在前面的评论中所指定的那样,通过这种方式,您将无法将参数传递给您的流程。

C实现

根据我的需要,我重新调整了@Gillespie的函数,以包括上述修改,并使用C编程语言:

FILE * custom_popen(char* command, char type, pid_t* pid)
{
    pid_t child_pid;
    int fd[2];
    pipe(fd);
    if((child_pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }
    /* child process */
    if (child_pid == 0)
    {
        if (type == 'r')
        {
            close(fd[0]);    //Close the READ end of the pipe since the child's fd is write-only
            dup2(fd[1], 1); //Redirect stdout to pipe
        }
        else
        {
            close(fd[1]);    //Close the WRITE end of the pipe since the child's fd is read-only
            dup2(fd[0], 0);   //Redirect stdin to pipe
        }
        setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
        execl(command, command, (char*)NULL);
        exit(0);
    }
    else
    {
        printf("child pid %dn", child_pid);
        if (type == 'r')
        {
            close(fd[1]); //Close the WRITE end of the pipe since parent's fd is read-only
        }
        else
        {
            close(fd[0]); //Close the READ end of the pipe since parent's fd is write-only
        }
    }
    *pid = child_pid;
    if (type == 'r')
    {
        return fdopen(fd[0], "r");
    }
    return fdopen(fd[1], "w");
}
int custom_pclose(FILE * fp, pid_t pid)
{
    int stat;
    fclose(fp);
    while (waitpid(pid, &stat, 0) == -1)
    {
        if (errno != EINTR)
        {
            stat = -1;
            break;
        }
    }
    return stat;
}