Отправляет email-рассылки с помощью сервиса Sendsay

Программирование и др.

  Все выпуски  

Программирование в Linux с нуля - Выпуск 11


Информационный Канал Subscribe.Ru

Рассылка проекта Lindevel.Ru

Программирование в Linux с нуля. Выпуск 11

1. Предисловие

Здравствуйте, уважаемые подписчики! Рассылка вернулась из летнего отпуска. Этот выпуск подводит итоги темы "Введение в многозадачность Linux". Сегодня мы напишем очень простенькую командную оболочку (shell). Если возникнут вопросы, задавайте их в форуме. Справочная информация и электронные учебники доступны в файловом архиве на Lindevel.Ru. Сайт вышел из отпуска и постоянно пополняется новыми материалами и программным обеспечением.

2. Программа

2.1. Описание

Итак, в этом выпуске мы рассматриваем простенькую командную оболочку (shell). Приведенный пример наглядно демонстрирует совместное использование системных вызовов fork() и exec(), а также знакомит вас с функцией wait(), имеющей непосредственное отношение к многозадачности в Linux. Представленная мини-оболочка располагает только одной встроенной командой exit. При изучении темы "Программное окружение", мы вернемся к рассмотрению этой оболочки и внедрим в нее команду cd. С компиляцией, сборкой и запуском разбирайтесь сами :-)

mysh.c
#define MAX_ARGS 16
#define MAX_LENGTH 256

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int run_p (char* const args [])
{
 pid_t npid = fork ();

 if (npid > 0) { return npid; }
 else if (npid == 0) {
  execvp (args[0], args);
  return -1;
 } else { abort (); }
}

int main (void)
{
 char str [MAX_LENGTH];
 char* buf[MAX_ARGS];
 int i, j, status;

 for (i = 0; i < MAX_ARGS; i++) { buf[i] = (char*) malloc (MAX_LENGTH); }

 printf ("mysh# ");
 
 while (fgets (str, MAX_LENGTH, stdin))
 {
  str[strlen(str)-1] = '\0';
  char* p = (char*) malloc (MAX_LENGTH);
  p = strtok (str, " ");

  if (!strcmp (p, "exit")) { exit (0); }

  for (i = 0; i < MAX_ARGS; i++) 
   { buf[i] = (char*) malloc (MAX_LENGTH); }
  
  for (i = 0; p != NULL; i++) {  
   strcpy (buf[i], p);   
   p = strtok (NULL, " ");
  }
  
  buf[i] = NULL;
  status = run_p (buf);

  if (status < 0) 
   { fprintf (stderr, "mysh: %s: command not found\n", buf[0]); 
   exit (1); }
  
  wait (&status);
  free (p);
  for (i = 0; i < MAX_ARGS; i++) { free (buf[i]); }
  printf ("mysh# ");
 };

 exit (0);
}


3. Теория

3.1. Запуск программ в отдельном процессе

Ранее мы рассматривали возможность порождать в Linux новые процессы функцией fork() и возможность "подсовывать" программе чужой код функциями из семейства exec(). Но чаще всего системные вызовы fork() и exec() используются в одном контексте. Их совместное использование позволяет запускать программы в одтельных процессах. Как это делается, вы уже, вероятно, догадались. Функция fork() создает отдельный процесс, продолжающий выполнять исходную программу. Отлавливая (при помощи тривиального ветвления) порожденный процесс, заставляем его выполнить exec() - и дело сделано! Чтобы "отловить" потомка, достаточно вспомнить то, что функция fork() возвращает в порожденный процесс нулевое значение. В рассмотренном примере функция run_p() наглядно демонстрирует вышеописанную стратегию, хотя это далеко не единственный способ запустить программу в отдельном процессе.

3.2. Блокировка процессов

Возьмем командную оболочку, в которой вы работаете, будь то bash, csh, Korn (ksh) или какая-нибудь другая. Запуская внешнюю программу, оболочка ждет, пока программа завершится и только после этого выдается приглашение на ввод следующей команды. Однако мы знаем, что процессы в Linux выполняются асинхронно, т. е. независимо, пытаясь обогнать друг друга и "выхватить" у системы как можно больше ресурсов для себя-любимого. Эта т. н. вытесняющая многозадачность не может гарантировать, например, что процесс A завершится раньше процесса B или наоборот. Все это очень хорошо, но иногда от программиста требуется синхронизировать работу нескольких процессов.

Программист может заморозить родительский процесс до тех пор, пока не завершится какой-нибудь его потомок. Делается это очень просто: в том месте, где процесс нужно заблокировать и ждать завершения потомка, вызывается одна из функций семейства wait(). Вот и все! В семейство wait() входят функции wait(), waitpid(), wait3(), wait4() и waitid(). Последние три характерны не для всех Unix-систем, используются редко и в ближайшее время мы их рассматривать не будем.

Функция wait() блокирует процесс до тех пор, пока не завершится один из его потомков, а функция waitpid() "ждет" завершения конкретного процесса. Все функции семейства wait() возвращают идентификатор завершившегося дочернего процесса, если только не было ошибок.

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

Функция waitpid() вызывается с тремя аргументами: идентификатор ожидаемого дочернего процесса, статус завершенного процесса (указатель) и целое число для установки некоторых параметров.

Использование функций семейства wait() будет подробно рассмотрено позже при детальном изучении многозадачности Linux. Ниже представлены адаптированные прототипы функций wait() и waitpid().

  • pid_t wait (int * stat_loc);
  • pid_t waitpid (pid_t pid, int * stat_loc, int options);

4.1. Разбор полетов

Рассмотрим наш пример, командную оболочку mysh. Механизм работы функции run_p() не должен вызвать вопросов, поскольку этот материал уже подробно изучался в предыдущих выпусках рассылки.

Любая уважающая себя оболочка должна запускать внешние программы и ожидать их завершения. В нашем случае программа запускается функцией run_p(). Если программа успешно запущена, вызывается функция wait(), которая блокирует оболочку до завершения дочернего процесса.

В качестве аргумента функции wait() был передан адрес первой попавшейся переменной status. Полученное значение никак не используется, но эта тема будет рассматриваться в последующих выпусках рассылки.

Сегодняшний выпуск завершает тему "Введение в многозадачность Linux". Теперь мы приступаем к теме "Программное окружение". Настоятельно рекомендую вам ликвидировать все пробелы в знаниях по предыдущим темам, поскольку теперь я не буду "разжевывать" материал так подробно, как это было раньше.

До новых встреч.
Николай.
mailto: nnivanov@mail.ru

Новости сайта Lindevel.Ru

  • До конца сентября на сайте планируется организовать работу коллективных проектов по разработке программного обеспечения. Жду ваших пожеланий в форуме.
  • Рассылка FreeBSD скоро вернется из отпуска.

Рассылки Subscribe.Ru
*nix project - новости из мира unix-систем! (ежедневная)

Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.linuxp
Отписаться
Вспомнить пароль

В избранное