Главная страница  Межпроцессное взаимодействие (состязание) 

1 2 3 4 5 6 7 8 9 10 11 [ 12 ] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

вы STAT и FSTAT. Они различаются только тем, что первый из них требует имени файла, а второй полагается на дескриптор файла и полезен для открытых файлов, особенно для файлов стандартного ввода и вывода, имена которых не всегда известны. У обоих вызовов второй параметр указывает на структуру, куда нужно поместить информацию. Эта структура показана на листинге 1.2.

Листинг 1.2. Структура, используемая для получения информации от системных вызовов STAT и FSTAT

struct Stat {

short st dev: /*устройство. которому принадлежит inode*/

unsigned short st ino: /*номер inode*/

unsigned short st mode; /*режим доступа*/

short st nlink: /*число ссылок на файл*/

short st u1d: /*идентификатор пользователя*/

short st gid: /*идентификатор группы*/

short st rdev: /*основное и вторичное */

/*устройство для специальных файлов*/ long st size: /*размер файла*/

long st atime: /*время последнего обращения*/

long st tntime: /*время последнего изменения*/

long st ctime; /*время последнего изменения inode*/

При работе с файловыми дескрипторами изредка может оказаться полезным системный вызов DUP. Например, рассмотрим программу, которая закрывает стандартный вывод (дескриптор 1), подставляет в качестве стандартного вывода другой файл, вызывает функцию, которая что-то пишет в этот файл, а затем восстанавливает исходное состояние. Если просто закрыть стандартный вывод и открыть новый файл, то новый файл станет стандартным выводом (если стандартный ввод, дескриптор которого равен О, используется), но восстановить состояние будет невозможно. Решение дает следующий оператор, который использует системный вызов DUP:

fd = dup(l):

При его выполнении в переменную fd помещается новый дескриптор, который будет соответствовать тому же файлу, что и стандартный вывод (1). Затем стандартный вывод можно закрыть, после чего открыть новый файл и использовать его. Когда понадобится восстановить исходное состояние, нужно закрыть файловый дескриптор 1 и выполнить код:

п = dup(fd):

В результате самый меньший из файловых дескрипторов, а именно 1, станет соответствовать тому же файлу, что и fd. Наконец, fd можно закрыть, в результате чего мы вернемся к той же ситуации, с которой начали.

У системного вызова dup есть второй вариант, с помощью которого можно связать неиспользованный дескриптор с уже открытым файлом. Он записывается так:

dup2(fd. fd2):

Здесь fd это дескриптор открытого файла, а fd2 - не связанный ни с каким файлом дескриптор, который после выполнения вызова будет ссылаться на тот



же файл. Таким образом, если fd ссылается на стандартный ввод (дескриптор равен 0), а дескриптор fd2 равен 4, то после выполнения вызова и О, и 4 будут соответствовать стандартному вводу.

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

cat filel file2 sort

то оболочка создает канал и соединяет стандартный вывод первого процесса со входом канала, а стандартный ввод второго процесса с выходом. Чтобы создать канал, применяется системный вызов pipe, возвращающий два файловых дескриптора: один для чтения из канала и другой для записи в него. Обычно после этого делается вызов fork, и родительский процесс закрывает дескриптор для чтения, а дочерний процесс - дескриптор для записи (или наоборот), чтобы один процесс мог писать в канал, а другой - читать из него.

В листинге 1.3 приведен скелет процедуры, создающей два процесса таким образом, что выход первого из них передается через канал во второй (более реалистичный пример делал бы проверку ошибок и обработку аргументов). Сначала создается поток, затем делается вызов fork, и родительский процесс становится первым процессом в канале, а дочерний процесс - вторым. Так как запускаемые файлы, processl и process2, ничего не знают о том, что они соединяются каналом, для работы программы необходимо, чтобы стандартный вывод первого процесса был соединен со стандартным вводом второго процесса каналом. Сначала родительский процесс закрывает дескриптор для чтения из канала. Затем он закрывает стандартный вывод и делает вызов dup, после которого стандартным выводом становится вход канала. Важно понимать, что dup всегда возвращает наименьший возможный дескриптор, в данном случае это будет 1. Наконец, исходный дескриптор для записи в канал закрывается.

Листинг 1.3. Скелет процедуры, создающей конвейер из двух процессов

#define STD INPUT О /* Дескриптор файла для стандартного устройства ввода */

idefine STD OUTPUT 1 /* Дескриптор файла для стандартного устройства вывода*/

pipeline(processl. process2) /* Указатели на имена программ */

char *processl. *process2:

int fd[2];

pipe(&fd[0]): /* создать конвейер */

ifCforkO !=0) {

/* Эти выражения исполняются родительским процессом */

close{fd[0]); /* Процесс 1 не нуждается в считывании из конвейера */

c1ose(STD 0UTPUT): /* Подготовка нового стандартного устройства вывода */

dup(fd[l]): /* Сделать стандартным устройством вывода fd[l] */

close(fd[l]); /* Этот дескриптор файла больше не нужен */

execl(processl.processl.0):

} else {

/* Эти выражения исполняются процессом-наследником */ close(fd[l]): /* Процесс 2 не нуждается в записи в конвейер */

close(STD INPUT): /* Подготовка нового стандартного устройства ввода */

dup(fd[0]): /* Сделать стандартным устройством ввода fd[0] */



close(fd[0]): /* Этот дескриптор файла больше не нужен */

execl(process2.process2.0):

После вызова exec стартует процесс, у которого дескрипторы О и 2 остались без изменений, а дескриптор 1 соответствует записи в канал. Код дочернего процесса аналогичен. Значение параметра функции execl повторяется потому, что первым ее параметром должно быть имя программы, а вторым - значение первого аргумента запускаемой профаммы, которое для большинства программ должно совпадать с именем.

Следующий системный вызов, ioctl, потенциально применим ко всем специальным файлам. Например, он используется драйверами блочных устройств, таких как SCSI драйвера для работы с ленточными накопителями и CD-ROM. Тем не менее он в основном применяется к символьным специальным файлам, особенно к терминалам. В стандарте POSIX определено несколько функций, которые транслируются библиотекой в вызовы ioctl. С помощью функций tcgetattr и tcsetattr можно изменить характеристики терминала, такие как коррекция ошибок, режим и т. д. Эти функции используют вызов ioctl.

Режим с обработкой (cooked mode) - это нормальный режим работы терминала, в котором удаление символов работает нормально, символы Ctrl+S и Ctrl+Q соответственно останавливают и запускают вывод информации на терминал, Ctrl+D означает конец файла, а Del генерирует сигнал прерывания. Нажатие Ctrl+\ в этом режиме генерирует сигнал, по которому процесс принудительно прерывается, и выводится дамп памяти.

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

Режим cbreak (cbreak mode) - промежуточный между двумя предыдущими. В этом режиме при редактировании не работают символы удаления и Ctrl+D, но Ctrl+S, Ctrl+Q, Del и Ctrl+\ работают. Как и в сыром режиме, символы передаются профаммам сразу, не дожидаясь окончания ввода строки (так как редактирование строк не работает, не обязательно дожидаться окончания ввода, потому что пользователь не сможет передумать и удалить введенные символы, как в режиме с обработкой).

Термины cooked mode, raw mode и cbreak mode в стандарте POSIX не используются. Вместо этого POSIX определяет канонический режим (canonical mode), соответствующий режиму с обработкой. В нем определено одиннадцать специальных символов, и ввод ведется построчно. В неканоническом режиме (noncanonical mode) ввод данных определяется минимальным воспринимаемым количеством символов и временем (в десятых долях секунды). Стандарт POSIX очень гибок и определяет множество различных флагов, управляя которыми можно приблизить неканонический режим как к сырому* режиму, так и к режиму cbreak. Старые термины более содержательны, поэтому мы неформально будем придерживаться их.

У вызова ioctl есть три параметра. Например, вызов функции toetattr преобразуется в следующий вызов:

ioctl(fd. TCSETS. Stermios):



1 2 3 4 5 6 7 8 9 10 11 [ 12 ] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2018 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.