Читать интересную книгу UNIX: взаимодействие процессов - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать

15  return;

16 }

17 /* Фатальная ошибка при системном вызове.

18  * Вывод сообщения и завершение работы. */

19 void

20 err_sys(const char *fmt, ...)

21 {

22  va_list ap;

23  va_start(ap, fmt);

24  err_doit(1, LOG_ERR, fmt, ар);

25  va_end(ap);

26  exit(1);

27 }

28 /* Фатальная ошибка при системном вызове.

29  * Вывод сообщения, сохранение дампа памяти, завершение работы. */

30 void

31 err_dump(const char *fmt, ...)

32 {

33  va_list ар;

34  va_start(ap, fmt);

35  err_doit(1, LOG_ERR, fmt, ap);

36  va_end(ap);

37  abort(); /* сохранение дампа и завершение */

38  exit(1); /* досюда не должно дойти */

39 }

40 /* Нефатальная ошибка не при системном вызове.

41  * Вывод сообщения и возврат. */

42 void

43 err_msg(const char *fmt, ...)

44 {

45  va_list ap;

46  va_start(ap, fmt);

47  err_doit(0, LOG_INFO, fmt, ap);

48  va_end(ap);

49  return;

50 }

51 /* Фатальная ошибка не при системном вызове.

52  * Вывод сообщения и возврат. */

53 void

54 err_quit(const char *fmt, ...)

55 {

56  va_list ap;

57  va_start(ap, fmt);

58  err_doit(0, LOG_ERR, fmt, ap);

59  va_end(ap);

60  exit(1);

61 }

62 /* Вывод сообщения и возврат.

63  * Вызывающий указывает "errnoflag" и "level". */

64 static void

65 err_doit(int errnoflag, int level, const char *fmt, va_list ap)

66 {

67  int errno_save, n;

68  char buf[MAXLINE];

69  errno_save = errno; /* значение может понадобиться вызвавшему */

70 #ifdef HAVE_VSNPRINTF

71  vsnprintf(buf, sizeof(buf), fmt, ар); /* защищенный вариант */

72 #else

73  vsprintf(buf, fmt, ар); /* незащищенный вариант */

74 #endif

75  n = strlen(buf);

76  if (errnoflag)

77   snprintf(buf+n, sizeof(buf)-n, ": %s", strerror(errno_save));

78  strcat(buf, "n");

79  if (daemon_proc) {

80   syslog(level, buf);

81  } else {

82   fflush(stdout); /* если stdout и stderr одинаковы */

83   fputs(buf, stderr);

84   fflush(stderr);

85  }

86  return;

87 }

ПРИЛОЖЕНИЕ Г

Решения некоторых упражнений

Глава 1

1. В обоих процессах нужно лишь указать флаг O_APPEND при вызове функции open или режим дополнения файла при вызове fopen. Ядро гарантирует, что данные будут дописываться в конец файла. Это самая простая форма синхронизации доступа к файлу. На с. 60-61 [21] об этом рассказывается более подробно. Синхронизация становится проблемой при обновлении имеющихся в файле данных, как это происходит в базах данных.

2. Обычно встречается что-нибудь вроде:

#ifdef REENTRANT

#define errno (*_errno())

#else

extern int errno;

#endif

Если определена константа _REENTRANT, обращение к errno приводит к вызову функции _errno, возвращающей адрес переменной errno вызвавшего потока. Эта переменная, скорее всего, хранится в области собственных данных этого потока (раздел 23.5 [24]). Если константа REENTRANT не определена, переменная errno является глобальной.

Глава 2

1. Эти два бита могут менять действующий идентификатор пользователя и/или группы выполняющейся программы. Идентификаторы используются в разделе 2.4.

2. Сначала следует указать флаги O_CREAT | O_EXCL, и если вызов окажется успешным, будет создан новый объект. Если вызов вернет ошибку EEXIST, объект уже существует и программа должна вызвать open еще раз, без флага O_CREAT или O_EXCL Второй вызов должен оказаться успешным, но есть вероятность, что он вернет ошибку ENOENT, если какой-либо другой поток или процесс удалит объект в промежутке между этими двумя вызовами.

Глава 3

1. Текст пpoгрaммы приведен в листинге Г.1.[1]

Листинг Г.1. Вывод идентификатора и порядкового номера слота

//svmsg/slotseq.c

1  #include "unpipc.h"

2  int

3  main(int argc, char **argv)

4  {

5   int i, msqid;

6   struct msqid_ds info;

7   for (i = 0; i < 10; i++) {

8    msqid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);

9    Msgctl(msqid, IPC_STAT, &info);

10   printf("msqid = %d, seq = %lun", msqid, info.msg_perm.seq);

11   Msgctl(msqid, IPC_RMID, NULL);

12  }

13  exit(0);

14 }

2. Первый вызов msgget задействует первую свободную очередь сообщений, порядковый номер которой имеет значение 20 после двукратного запуска программы из листинга 3.2, и вернет идентификатор 1000. Если предположить, что следующая доступная очередь сообщений никогда ранее не использовалась, ее порядковый номер будет иметь значение 0, а возвращаться будет идентификатор 1.

3. Программа приведена в листинге Г.2.

Листинг Г.2. Проверка использования маски создания файла функцией msgget

//svmsg/testumask.c

1 #include "unpipc.h"

2 int

3 main(int argc, char **argv)

4 {

5  Msgget(IPC_PRIVATE, 0666 | IPC_CREAT | IPC_EXCL);

6  unlink("/tmp/fifo.1");

7  Mkfifo("/tmp/fifo.1", 0666);

8  exit(0);

9 }

Запустив эту пpoгрaммy, мы увидим, что маска создания файла имеет значение 2 (снять бит записи для прочих пользователей) и этот бит оказывается снятым для канала FIFO, но не для очереди сообщений:

solaris % umask

02

solaris % testumask

solaris % ls –l /tmp/fifo.1

prw-rw-r-- 1 rstevens other1 0 Mar 25 16:05 /tmp/fifo.1

solaris % ipcs –q

IPC status from <running system> as of Wed Mar 25 16:06:03 1998

T ID  KEY       MODE      OWNER    GROUP

Message Queues:

q 200 00000000 –rw-rw-rw– rstevens other1

4. При использовании ftok имеется вероятность того, что для двух полных имен получится один и тот же ключ. При использовании IPC_PRIVATE сервер знает, что он создает новую очередь, но в этом случае ему нужно записать ее идентификатор в какой-либо файл, чтобы клиенты могли его считать.

5. Вот один из способов обнаружения коллизий:

solaris % find / –links 1 –not –type l – print | xargs –n1 ftok1 > temp.1

solaris % wc –l temp.1

109351 temp.1

solaris % sort +0 –1 temp.1 | nawk '{ if (lastkey== $1) print lastline, $0 lastline = $0 lastkey = $1 }' > temp.2

solaris % wc –l temp.2 82188 temp.2

Программа find игнорирует файлы, на которые имеется несколько ссылок (поскольку у всех ссылок одинаковый номер узла), и символические ссылки (поскольку функция stat возвращает информацию для файла, на который ссылка указывает). Большой процент коллизий (75,2%) вызван тем, что в Solaris 2.x используется только 12 бит номера узла. Поэтому в файловых системах с числом файлов более 4096 количество коллизий может быть велико. Например, файлы с номерами 4096, 8192, 12288 и 16384 будут иметь один и тот же ключ IPC (если все они принадлежат одной файловой системе).

Мы запустили эту программу в той же файловой системе, но используя функцию ftok из BSD/OS, которая добавляет номер узла к ключу целиком, и получили всего 849 коллизий (менее 1%).

Глава 4

1. Если бы дескриптор fd[1] остался открытым в дочернем процессе при завершении родительского, его операция read для этого дескриптора не вернула бы признак конца файла, потому что дескриптор был бы еще открыт в дочернем процессе. Закрытие fd[1] гарантирует, что после завершения родительского процесса все его дескрипторы закрываются и вызов read для fd[1] возвращает 0.

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

3. Если выполнить

solaris % mainopen 2>temp.stderr

/etc/ntp.conf > /myfile

solaris % cat temp.stderr

sh: /myfile: cannot create

мы увидим, что popen срабатывает успешно, но fgets считывает символ конца файла. Сообщение об ошибке записывается интерпретатором в стандартный поток сообщений об ошибках.

5. Измените первый вызов open, указав флаг отключения блокировки:

readfifo = Open(SERV_FIFO, O_RDONLY | O_NONBLOCK, 0);

Возврат из этого вызова произойдет немедленно, как и из следующего вызова open, поскольку канал уже открыт на чтение. Чтобы избежать ошибки при вызове readline, флаг O_NONBLOCK для дескриптора readfifo следует снять, перед тем как вызывать эту функцию.

6. Если клиент откроет свой канал на чтение перед открытием канала сервера, все зависнет. Единственный способ избежать блокировки заключается в вызове open для этих двух каналов в порядке, показанном в листинге 4.11, или в использовании флага отключения блокировки.

7. Исчезновение пишущего процесса воспринимается считывающим как конец файла.

На этом сайте Вы можете читать книги онлайн бесплатно русская версия UNIX: взаимодействие процессов - Уильям Стивенс.

Оставить комментарий