我可以设置现有流程的流程组吗?

时间:2009-08-21 20:44:37

标签: linux

我有一堆迷你服务器进程正在运行。它们与我需要停止的FastCGI服务器位于同一个进程组中。 FastCGI服务器将终止其进程组中的所有内容,但我需要这些迷你服务器继续运行。

我可以更改正在运行的非子进程的进程组(它们是PID 1的子进程)吗? setpgid()以“没有这样的过程”失败,尽管我对此表示肯定。

这是在Fedora Core 10上。

注意这些进程已在运行。新服务器执行setsid()。这些是由旧代码生成的一些服务器,但没有。

3 个答案:

答案 0 :(得分:3)

你可以尝试的一件事是在miniservers中做setsid()。这将使他们成为会话流程组的领导者。

此外,请记住,您无法将进程组ID更改为另一个会话中的一个,并且您必须执行调用以从要更改该组的进程中更改进程组,或来自流程的母公司。

我最近编写了一些测试代码,用于定期更改一组进程的进程组,以执行非常类似的任务。你不需要定期更改组ID,只是因为我认为我可能会避开某个定期检查运行时间超过一定时间的组的脚本。它还可以帮助您跟踪setpgid()所带来的错误:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

void err(const char *msg);
void prn(const char *msg);
void mydaemon();

int main(int arc, char *argv[]) {

    mydaemon();
    if (setsid() < 0)
        err("setsid");

    int secs = 5*60;

    /* creating a pipe for the group leader to send changed
       group ids to the child */
    int pidx[2];
    if (pipe(pidx))
        err("pipe");

    fcntl(pidx[0], F_SETFL, O_NONBLOCK);
    fcntl(pidx[1], F_SETFL, O_NONBLOCK);

    prn("begin");

    /* here the child forks, it's a stand in for the set of
       processes that need to have their group ids changed */
    int child = fork();
    switch (child) {
    case -1: err("fork3");
    case  0:
        close(pidx[1]);

        while(1) {
            sleep(7);
            secs -= 7;
            if (secs <= 0) { prn("end child"); exit(0); }

            int pid;

            /* read new pid if available */
            if (read(pidx[0], &pid, sizeof pid) != sizeof pid) continue;

            /* set new process group id */
            if (setpgid(getpid(), pid)) err("setpgid2");

            prn("child group changed");
        }
    default: break;
    }

    close(pidx[0]);

    /* here the group leader is forked every 20 seconds so that
       a new process group can be sent to the child via the pipe */
    while (1) {
        sleep(20);

        secs -= 20;

        int pid = fork();
        switch (pid) {
        case -1: err("fork2");
        case  0:
            pid = getpid();

            /* set process group leader for this process */
            if (setpgid(pid, pid)) err("setpgid1");

            /* inform child of change */
            if (write(pidx[1], &pid, sizeof pid) != sizeof pid) err("write");

            prn("group leader changed");
            break;
        default:
            close(pidx[1]);
            _exit(0);
        }

        if (secs <= 0) { prn("end leader"); exit(0); }
    }
}

void prn(const char *msg) {
    char buf[256];
    strcpy(buf, msg);
    strcat(buf, "\n");
    write(2, buf, strlen(buf));
}

void err(const char *msg) {
    char buf[256];
    strcpy(buf, msg);
    strcat(buf, ": ");
    strcat(buf, strerror(errno));
    prn(buf);
    exit(1);
}

void mydaemon() {
    int pid = fork();
    switch (pid) {
      case -1: err("fork");
      case  0: break;
      default: _exit(0);
    }

    close(0);
    close(1);
    /* close(2); let's keep stderr */
}

答案 1 :(得分:1)

经过一番研究,我发现了它。 Inshalla遇到了一个基本问题,“你无法将进程组ID改为另一个会话”,这解释了为什么我的setpgid()失败了(带有误导性的消息)。但是,您似乎可以从组中的任何其他进程(不一定是父进程)更改它。

由于这些进程是由FastCGI服务器启动的,并且FastCGI服务器仍在运行且位于同一进程组中。因此,问题是,如果不杀死它产生的服务器,就无法重启FastCGI服务器。我写了一个新的CGI程序,它在正在运行的服务器上执行setpgid(),通过Web请求执行它并解决了问题!

答案 2 :(得分:0)

听起来你真的想要守护进程而不是移动进程组。 (注意:可以移动进程组,但我相信您需要处于同一个会话中,并且目标需要已经是一个进程组。)

但首先,看看daemonising是否适合你:

#include <unistd.h>
#include <stdio.h>

int main() {
  if (fork() == 0) {
    setsid();
    if (fork() == 0) {
      printf("I'm still running! pid:%d", getpid());
      sleep(10);
    }
    _exit(0);
  }

  return 0;
}

显然,您应该在实际代码中检查错误等,但上述情况应该有效。

即使主进程退出,内部进程也将继续运行。从/proc查看内部流程的状态,我们发现它确实是init的孩子:

Name:   a.out
State:  S (sleeping)
Tgid:   21513
Pid:    21513
PPid:   1
TracerPid:      0