Next Previous Contents

24. Управление заданиями

Управление заданиями относится к протоколу для разрешения пользователю двигаться между многими группами процессов (или работ) внутри одиночного сеанса входа в систему. Средства управления заданиями установлены так, чтобы соответствующее поведение для большинства программ устанавливалось автоматически и они не должны делать что-нибудь специальное относительно управления заданиями. Так что Вы можете возможно игнорировать материал этой главы, если Вы не пишите программу входа в систему или оболочку.

Вы должны быть знакомы с понятиями создания процесса (см. Раздел 23.2 [Понятия Создания Процесса]) и обработки сигналов (см. Главу 21 [Обработка Сигналов]) чтобы понять материал этой главы.

24.1 Понятия Управления заданиями

Фундаментальная цель интерактивной оболочки читать команды из терминала пользователя и создавать процессы, чтобы выполнить программы, заданные этими командами. Это можно делать использованием fork (см. Раздел 23.4 [Создание Процесса]) и exec (см. Раздел 23.5 [Выполнение Файла]) функций.

Одиночная команда может выполнять только один процесс, но часто одна команда использует отдельные процессы.

Если Вы используете оператор `|' в команде оболочки, Вы явно, запрашиваете несколько программ в их собственных процессах. Но даже если Вы выполняете только одну программу, она может использовать многократные процессы внутренне. Например, одиночная команда трансляции типа " cc -c foo .c " обычно использует четыре процесса. Если Вы выполняете make, ее работа - выполнить другие программы в отдельных процессах.

Процессы, принадлежащие одной команде называются группой процессов или работой. Для того, чтобы Вы могли функционировать на всех сразу. Например, печать C-c посылает сигнал SIGINT, чтобы завершить все процессы в приоритетной группе процессов.

Сеанс - большая группа процессов. Обычно все процессы одиночного входа в систему принадлежат тому же самому сеансу.

Каждый процесс принадлежит группе процессов. Когда процесс создан, он становится элементом той же самой группы процессов и сеанса как и родительский процесс. Вы можете помещать его в другую группу процессов, используя setpgid функцию, если группа процессов принадлежит тому же самому сеансу.

Единственый способ поместить процесс в другой сеанс состоит в том, чтобы сделать его начальным процессом нового сеанса или лидером сеанса, используя setsid функцию. Она также помещает лидера сеанса в новую группу процессов, и Вы не можете перемещать его вне этой группы процессов снова.

Обычно, новые сеансы создаются программой входа в систему, и лидер сеанса - процесс, выполняющий оболочку входа в систему пользователя.

Оболочка, которая поддерживает управление заданиями, должна знать, которая работа может использовать терминал в любое время. Иначе имелось бы сразу много работ, пробующих читать из терминала, и беспорядок относительно того, который процесс должен получить ввод, печатаемый пользователем. Чтобы предотвращать это, оболочка должна сотрудничать с драйвером терминала, используя протокол, описанный в этой главе.

Оболочка может давать безграничный доступ к терминалу управления только одной группы процессов одновременно. Она называется приоритетной работой на этом терминале. Другие группы процессов, управляемые оболочкой, которые выполняются без такого доступа к терминалу, называются фоновыми работами.

Если фоновая работа нуждается в чтении из или записи на терминал управления, она остановлена драйвером терминала. Пользователь может останавливать приоритетную работу, печатая символ SUSP (см. Раздел 12.4.9 [Специальные Символы]) и программа может останавливать любую работу, посылая ей сигнал SIGSTOP. Ответственность оболочки - обратить внимание на останов работ, сообщать пользователю относительно них, и обеспечивать механизмы для разрешения пользователю в интерактивном режиме продолжить остановленные работы и переключать работы между проритетной и фоновыми.

См. Раздел 24.4 [Доступ к Терминалу], для получения более подробной информации о терминале управления,

24.2 Управление Заданиями Необязательно

Не все операционные системы поддерживают управление заданиями. Система GNU поддерживает управление заданиями, но если Вы используете библиотеку GNU на некоторой другой системе, эта система может не поддерживать управление заданиями непосредственно.

Вы можете использовать _POSIX_JOB_CONTROL макрокоманду, чтобы проверить в во время компиляции, поддерживает ли система управление заданиями. См. Раздел 27.2 [Опции Системы].

Если управление заданиями не обеспечивается, то может быть только одна группа процессов на сеанс, который ведет себя, как будто он всегда приоритетный. Функции для создания дополнительных групп процессов просто терпят неудачу с кодом ошибки ENOSYS.

макрокоманды, именующие различные сигналы управления заданиями (см. Раздел 21.2.5 [Сигналы Управления заданиями]) определены, даже если управление заданиями не обеспечивается.

24.3 Управление Терминалом Процесса

Один из атрибутов процесса - терминал управления. Дочерние процессы, созданные с fork наследуют терминал управления из их родительского процесса. Таким образом,, все процессы в сеансе наследуют терминал управления от лидера сеанса. Лидер сеанса, который имеет контроль над терминалом, называется процессом управления этого терминала.

Вы вообще не должны волноваться относительно точного механизма, используемого, чтобы зарезервировать терминал управления сеанса, так как это выполнено для Вас системой когда Вы входите в систему.

Индивидуальный процесс отделяется от терминала управления, когда это вызывает setsid, чтобы стать лидером нового сеанса. См. Раздел 24.7.2 [Функции Группы процессов].

24.4 Доступ к Терминалу Управления

Процессы в приоритетной работе терминала управления имеют неограниченный доступ к этому терминалу; а фоновые процессы - нет. Этот раздел описывает более подробно, что случается, когда процесс в фоновой работе пробует обращаться к терминалу управления.

Когда процесс в фоновой работе пробует читать из терминала управления, группе процессов обычно послан сигнал SIGTTIN. Это обычно заставляет все процессы в той группе останавливаться (если они не обрабатывают сигнал и не останавливают себя). Однако, если процесс считывания игнорирует или блокирует этот сигнал, то происходит сбои read с EIO ошибкой.

Аналогично, когда процесс в фоновой работе пробует писать на терминал управления, заданное по умолчанию поведение - послать сигнал SIGTTOU группе процессов. Однако, поведение изменяется TOSTOP битом флагов автономных режимов (см. Раздел 12.4.7 [Автономные режимы]). Если этот бит не установлен (по умолчанию), то запись на терминал управления всегда разрешается без сигнала. Запись также разрешается, если сигнал SIGTTOU игнорируется или блокируется процессом записи.

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

24.5 Свободные Группы процессов

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

Чтобы предотвратить проблемы, группы процессов, которые продолжают выполняться даже после завершения лидера сеанса, отмечены как свободные группы процессов. Процессы в свободной группе процессов не могут читать из или писать на терминал управления. Это вызовет EIO ошибку.

Когда группа процессов становится свободной, процессам послан сигнал SIGHUP. Обычно, это заставляет процессы завершиться. Однако, если программа игнорирует этот сигнал или устанавливает обработчик для него (см. Главу 21 [Обработка Сигнала] ), она может продолжать выполнять свободную группу процессов даже после того, как процесс управления завершается.

24.6 Выполнение Оболочки Управления заданиями

Этот раздел описывает то, что оболочка должна делать, чтобы выполнить управление заданиями, обеспечивая протяженную типовую программу, чтобы проиллюстрировать включаемые понятия.

Структуры Данных для Оболочки

Все примеры программы, включенные в эту главу - часть простой программы оболочки. Этот раздел представляет структуры данных и сервисные функции, которые используются в примере.

Типовая оболочка имеет дело в основном с двумя структурами данных. Тип job содержит информацию относительно работы, которая является набором подпроцессов, связанных вместе трубопроводами. Тип process содержит информацию относительно одиночного подпроцесса. Имеются релевантные объявления структуры данных:

                typedef struct process
                {
                        struct process *next; 
                        char **argv;     
                        pid_t pid;       
                        char completed;      
                        char stopped;         
                        int status;     
                } process;
                typedef struct job
                {
                        struct job *next;   
                        char *command;        
                        process *first_process;   
                        pid_t pgid;        
                        char notified;       
                        struct termios tmodes; 
                        int stdin, stdout, stderr; 
                } job;
                job *first_job = NULL;
Имеются некоторые сервисные функции, которые используются для оперирования объектами job.
                job *
                find_job (pid_t pgid)
                {
                        job *j;
                        for (j = first_job; j; j = j->next)
                                if (j->pgid == pgid)
                                        return j;
                        return NULL;
                }
                int
                job_is_stopped (job *j)
                {
                        process *p;
                        for (p = j->first_process; p; p = p->next)
                                if (!p->completed && !p->stopped)
                                        return 0;
                        return 1;
                }
                int
                job_is_completed (job *j)
                {
                        process *p;
                        for (p = j->first_process; p; p = p->next)
                                if (!p->completed)
                                        return 0;
                        return 1;
                }

Инициализация Оболочки

Когда программа оболочки, которая обычно выполняет управление заданиями, начата, она должна быть внимательна в случае, если она вызвалось из другой оболочки, которая уже делает собственное управление заданиями.

Подоболочка, которая выполняется в интерактивном режиме, должна убедиться, что она была помещена на передний план родительской оболочкой прежде, чем она может давать возможность управлению заданиями непосредственно. Она делает это, получая начальную ID группы процессов с getpgrp функцией, и сравнивая его с ID группы процессов текущей приоритетной работы, связанной с терминалом управления (который может быть восстановлен, использованием функции tcgetpgrp).

Если подоболочка не выполняется как приоритетная работа, она должна остановить себя, посылая сигнал SIGTTIN собственной группе процессов. Она не может произвольно помещаться на передний план; она должна ждать пока пользователь сообщит родительской оболочке сделала это. Если подоболочка продолжена снова, она должна повторить проверку и останов непосредственно снова, если она все еще не на переднем плане.

Если только подоболочка была помещена на передний план родительской оболочкой, она может давать возможность собственному управлению заданиями. Она делает это, вызывая setpgid, чтобы поместить себя в собственную группу процессов, и вызывая tcsetpgrp, чтобы поместить эту группу процессов на передний план.

Когда оболочка дает возможность управлению заданиями, она должна установить себя, чтобы игнорировать все сигналы останова управления заданиями так, чтобы она случайно не остановила себя. Вы можете сделать это, устанавливая действие для всех сигналов останова как SIG_IGN.

Подоболочка, которая выполняется не-в интерактивном режиме, не может и не должна поддерживать управление заданиями. Она должна оставить все процессы, которые она создает в той же самой группе процессов как оболочка непосредственно; это позволяет родительской оболочке обрабатывать не-интерактивную оболочку и дочерние процессы как одиночную работу. Это просто сделать, только не используйте любой из примитивов управления заданиями, но Вы не должны забыть заставить оболочку делать это.

Вот код инициализации для типовой оболочки, который показывает, как сделать все это.

                #include <sys/types.h>
                #include <termios.h>
                #include <unistd.h>
                pid_t shell_pgid;
                struct termios shell_tmodes;
                int shell_terminal;
                int shell_is_interactive;
                void
                init_shell ()
                {
                        shell_terminal = STDIN_FILENO;
                        shell_is_interactive=isatty(shell_terminal);
                        if (shell_is_interactive)
                        {
                                while (tcgetpgrp (shell_terminal) !=    
                                        (shell_pgid = getpgrp ()))
                                kill (- shell_pgid, SIGTTIN);
                                signal (SIGINT, SIG_IGN);
                                signal (SIGQUIT, SIG_IGN);
                                signal (SIGTSTP, SIG_IGN);
                                signal (SIGTTIN, SIG_IGN);
                                signal (SIGTTOU, SIG_IGN);
                                signal (SIGCHLD, SIG_IGN);
                                shell_pgid = getpid ();
                                if (setpgid(shell_pgid,shell_pgid) < 0)
                                {
                                        perror ("Couldn't put the shell         
                                        in its own process group");
                                        exit (1);
                                }
                                tcsetpgrp (shell_terminal, shell_pgid);
                                tcgetattr(shell_terminal,&shell_tmodes);
                        }
                }

Запуск Работ

Если только оболочка приняла ответственность за выполнение управления заданиями на терминале управления, она может начинать работы в ответ на команды, печатаемые пользователем.

Чтобы создавать процессы в группе процессов, Вы используете те же самые fork и exec, описанные в Разделе 23.2 [Понятия Создания Процесса]. Так как имеются многократные дочерние включаемые процессы, Вы должны быть внимательны, чтобы делать дела в правильном порядке.

Вы имеете два выбора для того, как структурировать дерево родитель - дочерних связей среди процессов. Вы можете либо делать все процессы в группе процессов дочерними процесса оболочки, либо Вы можете делать один процесс в группе предком всех других процессов в той группе. Типовая программа оболочки, обеспеченная в этой главе использует первый подход, потому что это кажется несколько более простым.

При раздвоении процесса, он должен помещаться в новую группу процессов, вызовом setpgid; см. Раздел 24.7.2 [Функции Группы процессов]. Первый процесс в новой группе становится лидером группы процессов, и его ID, становится ID группы процессов.

Оболочка должна также вызвать setpgid, чтобы поместить каждый из дочерних процессов в новую группу процессов.

Имеется потенциальная проблема синхронизации: каждый дочерний процесс должен быть помещен в группу процессов прежде, чем он начинает выполнять новую программу, и оболочка зависит от наличия всех дочерних процессов в группе прежде, чем она продолжает выполняться. Если и дочерние процессы и оболочка вызывает setpgid, это гарантирует, что все будет правильно независимо от того, который процесс принимается за это раньше.

Если работа начинается как приоритетная работа, новая группа процессов также, должна быть помещена на передний план на терминале управления, используя tcsetpgrp. Снова, это должно быть выполнено оболочкой также как и каждым из дочерних процессов, чтобы избежать условий состязания.

Следующая вещь, которую каждый дочерний процесс должен делать, - сбросить действия сигналов.

В течение инициализации, процесс оболочки устанавливает себя, чтобы игнорировать сигналы управления заданиями; см. Раздел 24.6.2 [Инициализация Оболочки]. В результате, любые дочерние процессы, которые он создает, игнорируют эти сигналы наследованием. Это нежелательно, так что каждый дочерний процесс должен явно установить действия для этих сигналов обратно к SIG_DFL после того, как он раздвоено.

Так как оболочки следуют за этим соглашением, приложения могут принимать, что они наследуют правильную обработку этих сигналов из родительского процесса. Но каждое приложение не должно изменять обработку сигналов останова. Приложения, которые отключают нормальную интерпретацию символа SUSP, должны обеспечить некоторый другой механизм для пользователя, чтобы остановить работу. Когда пользователь вызывает этот механизм, программа должна послать сигнал SIGTSTP группе процессов, а не только на процесс непосредственно. См. Раздел 21.6.2 [Передача сигналов Другому Процессу].

В заключение, каждый дочерний процесс должен вызвать exec нормальным способом. Это - также точка, в которой должна быть обработана переадресация стандартного ввода и каналов вывода. См. Раздел 8.8 [Дублирование Описателей], для объяснения того, как делать это.

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

                void
                launch_process (process *p, pid_t pgid,
                                int infile, int outfile, int errfile,
                                int foreground)
                {
                        pid_t pid;
                        if (shell_is_interactive)
                        {
                                pid = getpid ();
                                if (pgid == 0) pgid = pid;
                                        setpgid (pid, pgid);
                                if (foreground)
                                        tcsetpgrp (shell_terminal, pgid);
                                signal (SIGINT, SIG_DFL);
                                signal (SIGQUIT, SIG_DFL);
                                signal (SIGTSTP, SIG_DFL);
                                signal (SIGTTIN, SIG_DFL);
                                signal (SIGTTOU, SIG_DFL);
                                signal (SIGCHLD, SIG_DFL);
                        }
                        if (infile != STDIN_FILENO)
                        {
                                dup2 (infile, STDIN_FILENO);
                                close (infile);
                        }
                        if (outfile != STDOUT_FILENO)
                        {
                                dup2 (outfile, STDOUT_FILENO);
                                close (outfile);
                        }
                        if (errfile != STDERR_FILENO)
                        {
                                dup2 (errfile, STDERR_FILENO);
                                close (errfile);
                        }
                        execvp (p->argv[0], p->argv);
                        perror ("execvp");
                        exit (1);
                }
Если оболочка не выполняется в интерактивном режиме, эта функция, не делает ничего с группами процессов или сигналами. Не забудьте, что оболочка, не выполняющая управление заданиями должна хранить все подпроцессы в той же самой группе процессов что и оболочка непосредственно.

Вот функция, что фактически начинает полную работу. После создания дочерних процессов, эта функция вызывает некоторые другие функции, чтобы поместить недавно созданную работу на передний план (или как фон); они обсуждены в Разделе 24.6.4 [Приоритетный и Фоновые].

                void
                launch_job (job *j, int foreground)
                {
                        process *p;
                        pid_t pid;
                        int mypipe[2], infile, outfile;
                        infile = j->stdin;
                        for (p = j->first_process; p; p = p->next)
                        {
                                if (p->next)
                                {
                                        if (pipe (mypipe) < 0)
                                        {
                                                perror ("pipe");
                                                exit (1);
                                        }
                                        outfile = mypipe[1];
                                }
                                else
                                        outfile = j->stdout;
                                pid = fork ();
                                if (pid == 0)
                                        launch_process(p, j->pgid,infile,    
                                                outfile, j->stderr,          
                                        foreground);
                                else if (pid < 0)
                                {
                                        perror ("fork");
                                        exit (1);
                                }
                                else
                                {
                                        p->pid = pid;
                                        if (shell_is_interactive)
                                        {
                                                if (!j->pgid)
                                                        j->pgid = pid;
                                                setpgid (pid, j->pgid);
                                        }
                                }
                                if (infile != j->stdin)
                                        close (infile);
                                if (outfile != j->stdout)
                                        close (outfile);
                                infile = mypipe[0];
                        }
                        format_job_info (j, "launched");
                        if (!shell_is_interactive)
                                wait_for_job (j);
                        else if (foreground)
                                put_job_in_foreground (j, 0);
                        else
                                put_job_in_background (j, 0);
                }

Приоритетный и Фоновые

Теперь давайте рассматривать, какие же действия должны предприниматься оболочкой, когда она начинает приоритетную работу (на переднем плане), и как это отличается от того, что должно быть выполнено, когда начинается фоновая работа.

Когда начинается приоритетная работа, оболочка должна сначала дать ей доступ к терминалу управления, вызывая tcsetpgrp. Затем, оболочка должна ждать завершения или останова процессов в этой группе процессов. Это обсуждено более подробно в Разделе 24.6.5 [Останов и Завершенные Работы].

Когда все процессы в группе завершились или остановились, оболочка должна восстановить контроль над терминалом для собственной группы процессов, вызывая tcsetpgrp снова. Так как сигналы останова вызванны вводом - выводом из фонового процесса или символом SUSP, печатаемым пользователем посланы группе процессов, обычно все процессы работы останавливаются вместе.

Приоритетная работа может оставить терминал в странном состоянии, так что оболочка должна восстановить собственные сохраненные режимы терминала перед продолжением. В случае, если работа просто остановлена, оболочка должна сначала сохранить текущие режимы терминала так, чтобы она могла восстанавливать их позже, если работа будет продолжена. Функции для обработки режимов терминала - tcgetattr и tcsetattr; они описаны в Разделе 12.4 [Режимы Терминала].

Вот функция оболочки для выполнения всего этого.

                void
                put_job_in_foreground (job *j, int cont)
                {
                        tcsetpgrp (shell_terminal, j->pgid);
                        if (cont)
                        {
                                tcsetattr (shell_terminal, TCSADRAIN,   
                                                &j->tmodes);
                                if (kill (- j->pgid, SIGCONT) < 0)
                                        perror ("kill (SIGCONT)");
                        }
                        wait_for_job (j);
                        tcsetpgrp (shell_terminal, shell_pgid);
                        tcgetattr (shell_terminal, &j->tmodes);
                        tcsetattr (shell_terminal, TCSADRAIN,           
                                &shell_tmodes);
                }
Если группа процессов начата как фоновая работа, оболочка должна остаться на переднем плане непосредственно и продолжить читать команды с терминала.

Вот функция, которая должна быть выполнена, чтобы поместить работу в фон:

                void
                put_job_in_background (job *j, int cont)
                {
                        if (cont)
                                if (kill (-j->pgid, SIGCONT) < 0)
                                        perror ("kill (SIGCONT)");
                }

Останов и Завершенные Работы

Когда начат приоритетный процесс, оболочка должна блокироваться, пока все процессы в этой работе не завершились или не остановились. Она может делать это, вызывая waitpid функцию; см. Раздел 23.6 [Завершение Процесса]. Используйте WUNTRACED опцию, чтобы состояние было сообщено и для процессов, что останавливаются, и для процессов, которые завершаются.

Оболочка должна также проверить состояния фоновых работ так, чтобы она могла сообщать о завершении или останове работы пользователю; это может быть выполнено вызовом waitpid с WNOHANG опцией. Хорошее место, чтобы поместить такую проверку для завершенных и остановленных работ - перед запросом новой команды.

Оболочка может также получать асинхронное уведомление, что имелась информация состояния дочернего процесса, устанавливая обработчик для сигналов SIGCHLD. См. Главу 21 [Обработка Сигнала].

В типовой программе оболочки, сигнал SIGCHLD обычно игнорируется. Чтобы избежать проблемы повторной входимости включая обработку глобальных данных структурировали оболочку. Но в определенных местах, когда оболочка не использует эти структуры данных, например, когда она ждет ввод на терминале, имеет смысл давать возможность обработчику для SIGCHLD. Та же самая функция, которая используется, чтобы делать синхронные проверки состояния может также вызываться изнутри этого обработчика.

Имеются части типовой программы оболочки, которые имеют дело с проверкой состояния работ и сообщением информации пользователю.

        int
        mark_process_status (pid_t pid, int status)
        {
                job *j;
                process *p;
                if (pid > 0)
                {
                        for (j = first_job; j; j = j->next)
                                for (p =j->first_process; p ;p=p->next)
                                        if (p->pid == pid)
                                        {
                                                p->status = status;
                                                if (WIFSTOPPED (status))
                                                        p->stopped = 1;
                                                else
                                                {
                                                  p->completed = 1;
                                                  if (WIFSIGNALED (status))
                                                  fprintf (stderr, "%d:         
                                                        Terminated by           
                                                signal %d.\n",                  
                                        (int) pid,                                      
                WTERMSIG (p->status));
                                                }
                                                return 0;
                                        }
                                        fprintf (stderr, "No child              
                                        process %d.\n", pid);
                                        return -1;
                }
                else if (pid==0 || errno==ECHILD)
                        return -1;
                else {
                        perror ("waitpid");
                        return -1;
                }
        }

        void
        update_status (void)
        {
                int status;
                pid_t pid;
                do
                        pid = waitpid (WAIT_ANY, &status,                   
                WUNTRACED|WNOHANG);
                while (!mark_process_status (pid, status));
        }
        void
        wait_for_job (job *j)
        {
                int status;
                pid_t pid;
                do
                        pid = waitpid (WAIT_ANY, &status, WUNTRACED);
                while (!mark_process_status (pid, status)
                                        && !job_is_stopped (j)
                                        && !job_is_completed (j));
        }
        void
        format_job_info (job *j, const char *status)
        {
                fprintf (stderr, "%ld (%s): %s\n", (long)j->pgid,    
                                status, j->command); 
        }
        void
        do_job_notification (void)
        {
                job *j, *jlast, *jnext;
                process *p;
                update_status ();
                jlast = NULL;
                for (j = first_job; j; j = jnext)
                {
                        jnext = j->next;
                        if (job_is_completed (j)) {
                                format_job_info (j, "completed"); 
                                if (jlast)
                                        jlast->next = jnext;
                                else
                                        first_job = jnext;
                                free_job (j);
                        }
                        else if (job_is_stopped (j) && !j->notified) { 
                                format_job_info (j, "stopped");
                                j->notified = 1;
                                jlast = j;
                        }
                        else
                                jlast = j;
                }
        }

Продолжение Остановленных Работ

Оболочка может продолжать остановленную работу, посылая сигнал SIGCONT группе процессов. Если работа продолжается на переднем плане, оболочка должна сначала вызвать tcsetpgrp, чтобы дать работе доступ к терминалу и восстановить сохраненные установки терминала. После продолжения работы на переднем плане, оболочка должна ждать останова или завершения работы, как будто работа только что была начата в переднем плане.

Типовая программа оболочки обрабатывает, и недавно созданные и непрерывные работы той же самой парой функций, put_job_in_foreground и put_job_in_background. Определения этих функций были даны в Разделе 24.6.4 [Приоритетный и Фоновые]. При продолжении остановленной работы, значение отлично от нуля передано как cont аргумент, чтобы гарантировать, что сигнал SIGCONT послан и режимы терминала установлены соответствующе.

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

                void
                mark_job_as_running (job *j)
                {
                        Process *p;
                        for (p = j->first_process; p; p = p->next)
                                p->stopped = 0;
                        j->notified = 0;
                }
                void
                continue_job (job *j, int foreground)
                {
                        mark_job_as_running (j);
                        if (foreground)
                                put_job_in_foreground (j, 1);
                        else
                                put_job_in_background (j, 1);
                }

Отсутствующие Части

Извлечения кода для типовой оболочки, включенные в эту главу - только часть всей программы оболочки. В частности ничто вообще не упоминалось относительно того, как размещена и инициализируется работа и структуры данных программы.

Более реальные оболочки обеспечивают сложный интерфейс пользователя, который имеет поддержку для языка команд; переменные; сокращения, замены, и сопоставление с образцом для имен файлов; и т.п.. Все это слишком сложно, чтобы объяснить здесь! Взамен, мы сконцентрировались на показе создания core-файла процесса и функций управления заданиями, которые могут вызываться из такой оболочки.

Вот таблица, подводящая итог основных точек входа, которые мы обеспечили:

       void init_shell (void)
Инициализирует внутреннее состояние оболочки. См. Раздел 24.6.2 [Инициализация Оболочки].

       void launch_job (job *j, int foreground)
Начинает работу j или как приоритетную или фоновую работа. См. Раздел 24.6.3 [Запуск Работ].
       void do_job_notification (void)
Проверяет и сообщает о любых работах, которые завершились или остановились. Может вызываться синхронно или внутри обработчика для сигналов SIGCHLD. См. Раздел 24.6.5 [Останов и Завершение Работы].
       void continue_job (job *j, int foreground)
Продолжает работу . См. Раздел 24.6.6 [Продолжение Остановленных Работ].

Конечно, реальная оболочка также должна бы обеспечивать другие функции для управления работами. Например, было бы полезно иметь команды, чтобы перечислить все текущие задания или послать сигнал (типа SIGKILL) к работе.

24.7 Функции для Управления заданиями

Этот раздел содержит детализированные описания функций в отношении управления заданиями.

Идентификация Терминала Управления

Вы можете использовать ctermid функцию, чтобы получить имя файла, которое Вы можете использовать, чтобы открыть терминал управления. В библиотеке GNU, она возвращает ту же самую строку все время: "/dev/tty". Это - специальное "волшебное" имя файла, которое относится к терминалу управления текущего процесса (если он его имеет). Функция ctermid объявлена в заглавном файле " stdio.h ".

       char * ctermid (char *string)  (функция)
Ctermid функция возвращает строку, содержащую имя файла терминала управления для текущего процесса. Если строка - не пустой указатель, это должен быть массив, который может содержать по крайней мере L_ctermid символов; строка возвращается в этом массиве. Иначе, возвращается указатель на строку в статической области, которая может быть перезаписана поверх при последующих обращениях к этой функции.

Пустая строка возвращена, если имя файла не может быть определено по любой причине. Даже если имя файла возвращено, доступ к файлу, который она представляет, не гарантируется.

       int L_ctermid  (макрос)
Значение этой макрокоманды - целочисленное постоянное выражение, которое представляет размер строки, достаточно большой, чтобы содержать имя файла, возвращенное ctermid.

См. также isatty и ttyname функции, в Разделе 12.1 [Терминал Ли Это].

Функции Группы процессов

Имеются описания функций для управления группами процессов. Ваша программа должна включить заглавные файлы " sys/types.h " и " unistd.h " чтобы использовать эти функции.

       pid_t setsid (void)  (функция)
Setsid функция создает новый сеанс. Вызывающий процесс становится лидером сеанса, и помещен в новую группу процессов, чей ID группа процессов тот же что и ID этого процесса. Не имеется первоначально никаких других процессов в новой группе процессов, и никаких других групп процессов в новом сеансе.

Эта функция также заставит вызывающий процесс не иметь никакого терминал управления.

Setsid функция возвращает ID новой группы процессов в случае успеха. Возвращаемое значение -1 указывает ошибку. Следующие errno условия ошибки определены для этой функции:

EPERM

Вызывающий процесс - уже лидер группы процессов, или имеется уже другая группа процессов, которая имеет тот же самый ID группы процессов.

Getpgrp функция имеет два определения: одно происходил от UNIX BSD, а одно от POSIX.1 стандарта. макрокоманды возможностей, которые Вы выбрали (см. Раздел 1.3.4 [Макрокоманды Возможностей]) определяют, которое определение Вы получаете. Вы получаете BSD версию, если Вы определяете _BSD_SOURCE; иначе, Вы получаете POSIX версию, если Вы определяете _POSIX_SOURCE или _GNU_SOURCE. Программы, которые пишутся для старых BSD систем не будут включать " unistd.h ", который определяет getpgrp для _BSD_SOURCE. Вы должны линковать такие программы с -lbsd-compat опцией, чтобы получить определение BSD.

       pid_t getpgrp (void)  (POSIX.1 функция)
POSIX. 1 определение getpgrp возвращает ID группы процессов вызывающего процесса.
       pid_t getpgrp (pid_t pid)  (BSD функция)
Определение BSD getpgrp возвращает ID группы процессов процесса pid. Вы можете обеспечивать значение 0 для pid аргумента, чтобы получить информацию относительно вызывающего процесса.
       int setpgid (pid_t pid, pid_t pgid)  (функция)
Setpgid функция помещает процесс pid в группу процессов pgid. Как частный случай, или pid или pgid может быть нуль, чтобы указать ID вызывающего процесса.

Эта функция терпит неудачу на системе, которая не поддерживает управление заданиями. См. Раздел 24.2 [Управление Заданиями Необязательно !], для подробной информации.

Если операция является успешной, setpgid, возвращает нуль. Иначе она возвращает -1. Следующие errno условия ошибки определены для этой функции:

EACCES

Дочерний процесс, именованный pid выполнил функцию exec после раздвоения.

EINVAL

Значение pgid не допустимо.

ENOSYS

Система не поддерживает управление заданиями.

EPERM

Процесс, обозначенный pid аргументом - лидер сеанса, или не в том же самом сеансе как вызывающий процесс, или значение pgid аргумента не соответствует ID группы процессов в том же самом сеансе как вызывающий процесс.

ESRCH

Процесс, обозначенный pid аргументом - не вызывающий процесс или дочерний из вызывающего процесса.

      int setpgrp (pid_t pid, pid_t pgid)  (функция)
Это - имя Unix BSD для setpgid. Обе функции делают точно то же самое.

Функции для Управления Доступом к Терминалу

Это функции для чтения или установки группы приоритетного процесса терминала. Вы должны включить заглавные файлы " sys/types.h " и " unistd.h " в вашем приложении, чтобы использовать эти функции.

Хотя эти функции берут аргумент-описатель файла, чтобы определить устройство терминала, приоритетная работа связана с файлом терминала непосредственно, а не с описателем открытого файла.

       pid_t tcgetpgrp (int filedes)  (функция)
Эта функция возвращает ID приоритетной группы процессов, связанной с терминалом, открытым на описателе filedes.

Если нее никакой приоритетной группы процессов, возвращаемое значение - число больше чем 1, которое не соответствует ни одному ID любой существующей группы процессов. Это может случаться, если все процессы в работе, которая была прежде приоритетная работа, завершились, и никакая другая работа не переместилась на передний план.

В случае ошибки возвращается значение -1. Следующие errno условия ошибки определены для этой функции:

EBADF

Filedes аргумент - не допустимый описатель файла.

ENOSYS

Система не поддерживает управление заданиями.

ENOTTY

Файл терминала, связанный с filedes аргументом не есть Терминал управления вызывающего процесса.

       int tcsetpgrp (int filedes, pid_t pgid)  (функция)
Эта функция используется, чтобы установить ID приоритетной группы процессов терминала. Аргумент filedes - описатель, который определяет терминал; pgid определяет группу процессов. Вызывающий процесс должен быть элементом того же самого сеанса как pgid и должен иметь тот же самый терминал управления.

Для целей доступа к терминалу, эта функция обрабатывается как вывод. Если она вызывается из фонового процесса на терминале управления, обычно всем процессам в группе процессов, послан сигнал SIGTTOU. Исключение - если вызывающий процесс непосредственно игнорирует или блокирует сигналы SIGTTOU, когда операция выполняется, и никакой сигнал не послан.

При успехе tcsetpgrp возвращает 0. Возвращаемое значение -1 указывает ошибку. Следующие errno условия ошибки определены для этой функции:

EBADF

Filedes аргумент - не допустимый описатель файла.

EINVAL

Pgid аргумент не допустим.

ENOSYS

Система не поддерживает управление заданиями.

ENOTTY

Filedes не терминал управления вызывающего процесса.

EPERM

Pgid не группа процессов в том же самом сеансе как вызывающий процесс.


Next Previous Contents