Позволяет ли Linux переназначать идентификаторы групп процессов процессам?

Предположим, что pid X является лидером группы процессов, а X завершается, но другие процессы в группе процессов продолжают работать (с X в качестве их pgid). Предотвратит ли Linux присвоение значения X в качестве pid новому процессу?

Я спрашиваю об этом из-за условия отказа, которое POSIX допускает для setsid:

[EPERM] Вызывающий процесс уже является лидером группы процессов, или идентификатор группы процессов процесса, отличного от вызывающего, совпадает с идентификатором вызывающего процесса.

Эта ошибка кажется неисправимым условием для кода, использующего группы процессов (то есть оболочки), которые будут запускаться «случайно», что делает ее еще более одиозной. Я бы предположил, что любая реализация, направленная на разумный уровень качества, будет избегать переназначения X в качестве pid, пока он все еще используется в качестве pgid, но я нигде не могу найти это задокументировано.


person R.. GitHub STOP HELPING ICE    schedule 22.07.2011    source источник
comment
Близкий избиратель, не могли бы вы объяснить, как знание поведения и возможных случаев ошибок для реализации не имеет отношения к ее программированию?   -  person R.. GitHub STOP HELPING ICE    schedule 22.07.2011


Ответы (2)


Не проблема, потому что форк гарантирует:

Идентификатор дочернего процесса также не должен совпадать с идентификатором какой-либо активной группы процессов.

И fork — единственный способ создать новые процессы.

person Nemo    schedule 22.07.2011
comment
Это, кажется, решает это. Есть идеи, почему этот фальшивый язык был включен в спецификацию для setsid, если он никогда не может применяться? - person R.. GitHub STOP HELPING ICE; 22.07.2011
comment
Строго говоря, fork — не единственный способ создания новых процессов. Также есть posix_spawn, posix_spawnp, popen и system. - person R.. GitHub STOP HELPING ICE; 20.07.2013
comment
О, и я понял, почему фиктивный язык в спецификации. Это не подделка. Если процесс, вызывающий setsid, является лидером группы процессов, то другие процессы в этой группе процессов будут иметь его идентификатор процесса в качестве идентификатора группы процессов. В этом случае setsid должен быть запрещен, потому что для нового сеанса требуется новый идентификатор сеанса и идентификатор группы процессов, которые будут равны pid вызывающей стороны. Но этот идентификатор нельзя использовать в качестве нового идентификатора группы процессов, поскольку группа процессов с таким идентификатором уже существует. Кстати, это причина, по которой daemon(3) традиционно удваивают fork. - person R.. GitHub STOP HELPING ICE; 20.07.2013
comment
@R: Извините, но я не понимаю. В спецификации говорится, что EPERM происходит, если (1) вызывающий процесс уже является лидером группы процессов или (2) идентификатор группы процессов процесса, отличного от вызывающего, совпадает с идентификатором вызывающего процесса. Ваше объяснение имеет смысл для (1), но (2) по-прежнему выглядит излишним/невозможным. Что мне не хватает? - person Nemo; 21.07.2013

Nemo прав в том, что POSIX гарантирует, что fork() не будет повторно использовать существующий PGID в качестве PID; однако это еще не все.

Группы процессов и лидеров групп процессов также можно изменить с помощью setpgid(). . Следующий пример кода вызывает существование группы процессов, равной PID текущего процесса, в которой текущий процесс отсутствует:

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

int main()
{
    pid_t pgrp_orig;
    pid_t child;
    int status;

    /* Fork so that we are not a process group leader */
    if (fork()) {
        /* Grandparent process */
        wait(&status);
        return 0;
    }

    /* Record our original process group, then start a new one */
    pgrp_orig = getpgrp();
    if (setpgid(0, 0))
        perror("setpgid");

    child = fork();

    if (!child) {
        /* Child process */
        pause();
        return 0;
    }

    /* Switch back to original process group.  Child remains in the new one */
    if (setpgid(0, pgrp_orig))
        perror("setpgid");

    printf("Parent pid=%ld, pgid=%ld\n", (long)getpid(), (long)getpgrp());
    printf("Child pid=%ld, pgid=%ld\n", (long)child, (long)getpgid(child));

    /* Wake child up to finish up */
    kill(child, SIGUSR1);
    wait(&status);

    return 0;
}

Обратите внимание, что если родительский процесс попытается вызвать здесь setsid() до того, как дочерний процесс завершится, сработает состояние сбоя, о котором вы спрашивали.

Однако из-за ограничений допустимых переходов, которые может вызвать setpgid(), это не может привести к случайным сбоям, о которых вы беспокоитесь. Поломка ограничивается одним сеансом.

person caf    schedule 22.07.2011
comment
В частности, setsid не может выйти из строя по этой причине, если вызывается сразу после fork в дочернем процессе, верно? - person R.. GitHub STOP HELPING ICE; 22.07.2011
comment
@R.: Это возможно возможно, но только в том случае, если родительский процесс пытается намеренно вызвать его (после разветвления родитель использует setpgid(), чтобы поместить дочерний процесс в свою собственную группу процессов, а затем использует setpgid() для поместить другой процесс в эту группу процессов, а затем использовать setpgid() в третий раз, чтобы вернуть дочерний процесс в исходную группу процессов). Очевидно, это тоже пикантно. - person caf; 22.07.2011