- •2.1. Введение
- •2.2. Вопросы, относящиеся к команде who
- •2.2.1. Программы состоят из команд
- •2.3. Вопрос 1: Что делает команда who?
- •2.3.1. Обращение к справочнику
- •2.4. Вопрос 2: Как работает команда who?
- •2.4.1. Мы теперь знаем, как работает who
- •2.5. Вопрос 3: Могу ли я написать who?
- •2.5.1. Вопрос: Как я буду читать структуры из файла?
- •2.5.2. Ответ: Использование open, read и close
- •2.5.3. Написание программы who 1.С
- •2.5.4. Отображение записей о вхождениях в систему
- •2.5.5. Написание версии who2.С
- •2.6. Проект два: Разработка программы ср (чтение и запись)
- •2.6.1. Вопрос 1: Что делает команда ср?
- •2.6.2. Вопрос 2: Как команда ср создает файл и как пишет в него? Создание/транкатенация файла
- •2.6.3. Вопрос 3: Могу ли я написать программу ср?
- •2.6.4. Программирование в Unix кажется достаточно простым
- •2.7. Увеличение эффективности файловых операций ввода/ вывода: Буферирование
- •2.7.1. Какой размер буфера следует считать лучшим?
- •2.7.2 Почему на системные вызовы требуется тратить время?
- •2.7.3. Означает ли, что наша программа who2.С неэффективна?
- •2.7.4. Добавление буферирования к программе who2.С
- •2.8. Буферизация и ядро
- •2.8.1. Если буферизация столь хороша, то почему ее не использует ядро?
- •2.9. Чтение файла и запись в файл
- •2.9.1. Выход из системы: Что происходит?
- •2.9.2. Выход из системы: Как это происходит
- •2.9.3. Смещение текущего указателя: Iseek
- •2.9.4. Кодирование выхода из системы через терминал
- •2.10. Что делать с ошибками системных вызовов?
- •3.1. Введение
- •3.2. Вопрос 1: Что делает команда is?
- •3.2.1. Команда Is выводит список имен файлов и оповещает об атрибутах файлов
- •3.2.3. Наиболее употребимые опции
- •3.2.4. Первый ответ: Итоговые замечания
- •3.3. Краткий обзор дерева файловой системы
- •3.4. Вопрос 2: Как работает команда Is?
- •3.4.1. Что же такое каталог, в конце концов?
- •3.4.2. Работают ли системные вызовы open, read и close в отношении каталогов?
- •3.4.3. Хорошо, хорошо. Но как же мне прочитать каталог?
- •3.5. Вопрос 3: Могу ли я написать Is?
- •3.5.1. Что еще нужно делать?
- •3.6. Проект 2: Написание версии Is -I
- •3.6.1. Вопрос 1: Что делает Is-I?
- •3.6.2. Вопрос 2: Какработает Is -I?
- •3.6.3. Ответ: Системный вызов stat получает информацию о файле
- •3.6.4. Какую еще информацию можно получить с помощью системного вызова stat?
- •3.6.5. Чего мы достигли?
- •3.6.6. Преобразование числового значения поля mode в символьное значение
- •3.6.7. Преобразования числового представления идентификаторов собственника/группы в строковое представление
- •3.6.8. Объединение всего вместе: Is2.C
- •3.7. Три специальных разряда
- •3.7.1. Разряд Set-User-id
- •3.7.2Разряд Set-Group-id
- •3.7.3 Разряд Sticky Bit
- •3.7.4. Специальные разряды nls-l
- •3.8. Итоги для команды is
- •3.9. Установка и модификация свойств файла
- •3.9.1. Тип файла
- •3.9.2. Разряды прав доступа и специальные разряды
- •3.9.3. Число ссылок на файл
- •3.9.4. Собственник и группа для файла
- •3.9.5. Размер файла
- •3.9.6. Время последней модификации и доступа
- •3.9.7. Имя файла
2.9.4. Кодирование выхода из системы через терминал
Теперь у нас есть все, что необходимо для написания функции, которая будет делать отметку в файле utmp при выходе из системы:
/*
* logout_tty(char *line)
* производит отметки в utmp - записи при выходе из системы
* не затирает имени пользователя и удаленной машины
* возвращает -1 - при ошибке, 0 - при успехе
*/
int logout ttyfchar *line)
{
int fd;
struct utmp rec;
int len = sizeoffstruct utmp);
int retval = -1; /* пессимизм */
if ((fd = open(UTMP_FILE,0_RDWR)) == -1) /* открытие файла 7
return -1;
/* поиск и замена */
while (readffd, &rec, len) == len)
if (strncmpfrec.ut line, line, sizeoffrec.ut line)) == 0)
{
rec.ut.type = DEAD_PROCESS; /* установка типа */
if (time(&rec.ut_time) != -1) /* и времени */
if (Iseekffd, -len, SEEK_CUR)!= -1) /* откат
if (writeffd, &rec, len) == len) /* модификация*/
retval = 0; /* успех! */
break;
}
/* закрытие файла */
if(close(fd)==-1)
retvai = -1;
return retvai;
}
В этом программном коде производится проверка возникновения ошибок для каждого системного вызова. В ваших системных программах следует всегда проверять наличие ошибок при каждом системном вызове. Такие программы в состоянии модифицировать файлы и данные, от которых зависит работа системы. Могут возникнуть серьезные последствия, если оставлять файлы в некотором противоречивом состоянии или оставлять их, не закончив с ними работу. С другой стороны, в ряде простых программ в данном тексте были опущены проверки на наличие ошибок. Это сделано было для того, чтобы оставить наглядной и ясной логику работу самих системных вызовов.
Упомянув об ошибках, теперь посмотрим, как управлять ими и как оповещать об их наличии.
2.10. Что делать с ошибками системных вызовов?
Если системный вызов open не может открыть файл, то он возвращает -1. Если системный вызов read не может прочитать данные, то он возвращает -1. Если системный вызов Iseek не может отыскать нужную позицию, то он возвращает -1. Системные вызовы возвращают -1, когда что-то выполняется неправильно. Ваши программы должны проверять код возврата каждого системного вызова, которые делаются в вашей программе, и предусматривать выполнение необходимых действий в случае возникновения ошибок.
Что следует считать неправильным? Для каждого системного вызова установлен собственный перечень ошибок. Рассмотрим системный вызов open. Файл может не существовать, вы можете не иметь прав на открытие файла или уже открыли слишком много файлов. Как же ваша программа сообщит вам, какая из нескольких возможных ошибок возникла?
Как идентифицировать ошибочную ситуацию: errno
Ядро оповещает вашу программу при возникновении ошибки с помощью определенного кода ошибки, который ядро записывает в глобальную переменную errno. Каждая программа имеет доступ к этой переменной.
На странице документации еггnо(З) и в заголовочном файле <errno.h> находятся символьное и числовое представление кодов ошибок. Вот несколько примеров:
#define |
EPERM |
1 |
/* Отсутствие прав на действие */ |
#define |
ENOENT |
2 |
/* Нет такого файла или каталога */ |
#define |
ESRCH |
3 |
/* Нет такого процесса */ |
#define |
EINTR |
4 |
/* Прерываемый системный вызов */ |
#define |
ЕЮ |
5 |
/* Ошибка ввода/вывода */ |
Различные ответы на различные ошибки
Вы можете использовать эти символические коды ошибок в вашей программе, когда будете программировать распознавание ошибочных ситуаций и действий, которые нужно предпринимать при ошибках. Это иллюстрируется в следующем программном коде:
tfinclude <errno.h>
extern int errno;
intsample()
{
intfd;
fd = openffile", О RDONLY);
if(fd==-1)
{
printf( "Cannot open file:");
if (errno ==ENOENT)
printf("There is no such file.");
else if (errno == EINTR)
printf("Interrupted while opening file.");
else if (errno == EACCESS)
printff You do not have permission to open file.");
…
Действия вашей программы будут зависеть от того, что вы будете считать ошибочным действием. Например, если системный вызов open заканчивается неуспешно, поскольку указанный файл не существует, вы можете запросить у пользователя другое имя файла.
С другой стороны, если программа открыла слишком много файлов (EMFILE), то можно закрыть некоторые из них и вновь попытаться открыть нужный файл. В данном случае пользователю нет необходимости знать о возникновении такой ошибки и выполненных действиях при ее возникновении.
Сообщения об ошибках: реггог(3)
Если вы хотите выдать сообщение, которое описывает ошибку, то требуется проверить значение переменной errno и вывести то или иное сообщение в зависимости от значения переменной. В функции sample, которая была приведена выше, это выполняется. Вместе с тем вместо указанных действий представляется удобным использовать библиотечную функцию perror(string). Функция perror(string) выбирает код ошибки и выводит в соответствии с возникшей стандартной ошибкой строку, которую вы ей передаете, вместе с кратким сообщением об ошибке.
В модифицированной версии программы sample используется perror:
int sample()
{
intfd;
fd - openffile", 0 RDONLY);
if f fd ===== -1)
{
Perror(“Cannot open file");
return;
}
Если возникает ошибка при работе системного вызова open, то вы увидите такого рода сообщения:
Cannot open file: No such file or directory
Cannot open file: Interrupted system call
В первой части диагностического вывода находится строка, которую вы передаете при I обращении к функции perror, а во второй части вывода находится текст, который соответ- I ствует коду ошибки в переменной errno.
Заключение
Основные идеи
• Команда who выводит список текущих пользователей после чтения его из системного журнала.
• В системах Unix данные хранятся в файлах. Unix - программы организуют передачу данных в файлы и из файлов с помощью шести системных вызовов:
open(filename, how)
creat(filename, mode)
read(fd, buffer, amt) writeffd, buffer, amt)
lseek(fd, distance, base) c
lose(fd)
• Процесс читает данные и записывает их с помощью файловых дескрипторов. Файловый дескриптор определяет соединение между процессом и файлом.
• Каждый раз, когда программа выполняет системный вызов, компьютер переключается из пользовательского режима в режим ядра. Далее исполняется некоторый код в ядре. Программы будут работать более эффективно, если в них будет произведена минимизация числа системных вызовов.
• Программы, которые читают и пишут данные, могут сократить число системных вызовов, размещая данные в буферах и обращаясь к ядру, когда необходимо записать заполненный буфер или поместить данные в буфер при его опустошении.
• Ядро Unix использует буферы, которые располагаются в памяти ядра для того, чтобы сократить время на пересылку данных между системой и диском.
• В Unix значение времени хранится в форме целого числа секунд, прошедших с момента начала работы Unix.
• Когда в Unix системный вызов обнаруживает ошибку, система устанавливает определенное значение в глобальной переменной errno и возвращает код возврата, равный -1. Системные программы могут использовать значение errno для диагностирования ошибок и выполнения необходимых действий при возникновении ошибок.
• Большая часть информации, которая была представлена в этом разделе, доступна в системе. Расширенные тексты документации представляют описание команд, что они делают, а в ряде случаев описывают, и как они работают. В заголовочных файлах содержатся определения структур данных, значения символических констант, прототипы функций, используемые для создания системных средств.
Исследования
2.1 Команда w. В Unix есть команда, которая называется w и которая имеет отношение к команде who. Попытайтесь выполнить команду и прочитайте документацию для нее. Какие действия поддерживаются в команде w и не поддерживаются в команде who? Какая при этом используется информация в файле utmp? Каково назначение дополнительной информации? Попытайтесь найти источники, объясняющие смысл дополнительной информации.
2.2 Авариии utmp. Когда вы входите в систему, то ваше входное имя, имя терминала, время, имя вашего удаленного хоста записываются в файл utmp. Когда вы выходите из системы, то запись зачищается. Что происходит, если в системе произойдет некая авария? Очевидно, что в файле utmp останется список пользователей, которые были в системе во время аварии. Когда система вновь стартует, то информация в файле utmp не будет верной. Что в системе Unix делается с файлом utmp, когда система стартует? Создаются ли записи для всех доступных терминальных линий? Может быть, создается пустой файл, в котором будут накапливаться записи о терминальных линиях? Для ответа на эти вопросы обратитесь к справочнику, заголовочным файлам и стартовым скриптам. Вы можете поэкспериментировать на собственных машинах.
2.3 Проверьте, как работает программа ср1, копируя некий файл на /dev/tty:
ср1 ср1.с/dev/tty. Здесь целевым файлом является терминал. Наша программа будет открывать терминал, производить запись и закрывать терминал, используя для этого те же системные вызовы, которые она использовала при посылке данных в файл на диске. Далее скопируйте данные с терминала в дисковый файл, используя такую нотацию: ср1 /dev/tty fily 1. Теперь ваша клавиатура становится входным файлом. Следует отметить, что после набора строк вы должны нажимать на клавишу Enter, а в конце следует набрать Ctrl-D.
2.4 Стандартные С - функции для работы с файлами fopen, getc, fclose, fgets представляют собой часть системы буферированного ввода и вывода файлов. Эти функции используют структуру типа FILE, которая является промежуточным уровнем и подобна по назначению модулю utmplib. Найдите определение FILE в заголовочных файлах, описание структур и сравните их с переменными в utmplib.с.
2.5 Запись в буферы ядра. Как убедиться в том, что данные, которые вы пишете на диск, действительно туда были записаны? Мы отмечали, что ядро будет копировать данные, когда оно в них нуждается. Изучите справочный материал, где говорится, как системные вызовы и программы отслеживают состояние буферов при копировании на
диск.
2.6 Многократное открытие одного и того же фата. В Unix допускается открытие одного файла несколькими процессами. В Unix возможно, что один и тот же процесс может многократно открывать один и тот же файл. Поэкспериментируйте с многократным открытием одного и того же файла, предварительно создав файл с некоторым произвольным текстом. Затем напишите программу, которая выполняет следующие действия:
(a) Открывает файл для чтения.
(b) Еще раз открывает этот же файл на запись.
(c) Еше раз открывает этот же файл на чтение
Вы должны получить три файловых дескриптора. После этого программа должна: I
(d) Прочитать 20 байтов, используя для этого первый дескриптор fd, и вывести наэкран то, что прочитали.
(e) Записать строку "testing 12 3", используя для этого второй дескриптор fd.
(f) Прочитать 20 байтов, используя для этого третий дескриптор fd и вывести на экран то, что прочитали.
2.7 Изучение электронного справочника. Команда man предоставляет вам информацию о командах Unix, системных вызовах, системных устройствах и информацию по другим темам. Какую команду следует использовать, чтобы изучить свойства самой команды man? Сколько разделов содержится в электронном справочнике в вашей версии Unix? Для чего они предназначены?
2.8 Изучение файла utmp. Файл utmp, о котором шла речь в предшествующих экспериментах, содержит записи, которые соотнесены текущим сессиям. Какие еще виды записей содержатся в этом файле? Для чего они предназначены?
2.9 Переход в конец файла. Системный вызов (seek позволяет вам выставить текущий указатель в файле на позицию, которая будет располагаться за концом файла. Например системный вызов:
lseek(fd,100,SEEKEND)
установит текущий указатель в позицию, которая смещена на 100 байтов от конца файла.
Что произойдет, если после этого вы попытаетесь читать данные сразу за концом файла? Что произойдет, если после этого вы попытаетесь писать данные сразу за концом файла? Попытайтесь записать некоторую строку, типа "hello", со смещением на большую величину относительно конца файла (например, 20 000 байтов). Проверьте, каков будет размер файла с помощью команды Is -I и с помощью команды Is -s. Что в результате получили?
Последняя трудная задача: Команда tail
Мы многое узнали при ознакомлении с рядом команд. Рассмотрите еще одну команду. У нас нет возможности ее изучать, поэтому вы должны будете сделать это самостоятельно.
Системный вызов Iseek позволяет вам передвигать текущий указатель по файлу. Вызов
lseek(fd,0,SEEK_END)
переместит текущий указатель в конец файла.
Команда tail позволяет отобразить последние десять строк файла. Попытайтесь ее выполнить. Команда tail будет выводить не от конца файла и вперед, а десять строк, которые располагаются перед концом файла. Заметим, что аргумент distance в системном вызове Iseek I измеряется в количестве символов.
Как работает команда tail? Разработайте собственную версию. Подумайте о буферировании, чтобы ваша программа работала эффективнее. Изучите документацию и познакомьтесь со всеми опциями, которые поддерживаются в команде tail. Как они работают? Разработка такой программы в отношении программ who и ср выглядит гораздо более простым I проектом.
Исходный код двух версий tail доступен на Web-сайте книги. Одна версия - это gnu версия, другая - версия bsd. В них используются различные средства. Прежде чем обратиться к этим решениям, попытайтесь написать одну из версий самостоятельно.
Свойства каталогов и файлов при
просмотре с помощью команды Is
Цели
Идеи и средства
• Каталог - это список файлов.
• Как прочитать каталог.
• Типы файлов и как определять тип файла.
• Свойства файлов и как определять свойства файл.
• Битовые наборы и биты маскирования.
• Идентификаторы пользователя, идентификаторы группы и база данных passwd.
Системные вызовы и функции
• opendir, readdir, closedir, seekdir
• stat
• chmod, chown, utime
• rename
Команды
• Is
