
- •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.5.5. Написание версии who2.С
В версии 2 нашей программы who внимание уделяется двум проблемам, о которых шла i речь в версии 1.
И вновь мы будем решать эти проблемы, обращаясь к необходимым документам справочника и заголовочным файлам.
Подавление пустых записей
В реальной версии команды who выводится список пользовательских имен тех пользователей, которые входили в систему. В нашей версии программы выводится список из того, что программа находит в файле utmp. Файл utmp содержит записи, касающиеся всех терминалов, даже тех, которые не используются. Необходимо изменить нашу программу так, чтобы она не выводила записи о неиспользуемых терминальных линиях. Но как определить, какая из utmp - записей не представляет активную сессию?
Самое простое решение (которое не работает) - пропускать записи с пробелами в поле пользовательского имени. Это будет работать в большинстве случаев, но на экран не будет выводиться запись с полем LOGIN в строке, которая относится к консоли. Лучшим решением (которое работает) будет, если выбирать для вывода только те utmp - записи, которые соответствуют пользователем, вошедшим в систему.
Обратимся к файлу /usr/include/utmp.h и мы обнаружим там следующее:
Г Определения для ut type */ |
|
|
«define EMPTY |
0 |
|
«define RUN LVL |
1 |
|
«define BOOT TIME |
2 |
|
«define OLD flME |
3 |
|
«define NEW TIME |
4 |
|
«define INIT PROCESS |
5 |
/* Процесс был порожден процессом "init" */ |
«define LOGIN PROCESS |
6 |
Г Процесс "getty" ждет login */ |
«define USER PROCESS |
7 |
/* Пользовательский процесс */ |
«define DEAD PROCESS |
8 |
|
Этот список весьма полезен. В каждой записи есть поле с именем utjype. Значения, которые могут находиться в этом поле, и их символические имена представлены в приведенном выше списке. Тип 7 будет для нас счастливым номером. Если теперь мы сделаем нижеследующие небольшие изменения в нашей функции showjnfo, то пробельные записи должны исчезнуть:
,
show info(struct utmp *utbufp)
{
if (utbufp->ut_type != USER_PROCESS) j* только пользователи! */
return;
printf("%-8.8s", utbufp->ut_name); /* имя пользователя */
Отображение времени вхождения в систему в удобном для прочтения виде
Теперь решим проблемы представления времени в формате, который воспринимаем людьми. Начнем поиск в справочнике и поиск заголовочных файлов. Страниц по теме "time" во всех версиях Unix весьма много, и они разнообразны.
После набора
$ man-k time
получим много записей. На одной своей машине я получил 73 записи, а на другой машине получил 97. Вы можете просмотреть этот длинный список или можете отфильтровать полученный вывод. Следующие ниже конвейеры прекрасно проведут фильтрацию:
$ man -k time | grep transform
$ man -к time | grep -i convert
Через справочник выходим на необходимые заголовочные файлы. Файл /usr/include/time.h есть на ряде систем Unix. Проверьте вашу систему относительно информации, касающейся темы “time”. Нам же нужно обсудить вопрос
Как в Unix хранится значение времени: тип данных time_t
В Unix значение времени представляется целым числом, которое измеряет в секундах интервал времени с полуночи первого января 1970 года по Гринвичу. Тип данных timej -это целочисленное представление времени в секундах. Этот формат в Unix используется во многих приложениях. Поле ut_time в utmp записях содержит время вхождения в систему, которое представлено числом секунд с начала Эпохи.
Преобразование time_t в читаемый формат: ctime
Есть функция ctime, которая преобразует значение времени в секундах от начала работы системы Unix в значение времени в читабельном формате. Функция описана в разделе 3 электронного справочника.
$ man 3 ctime |
|
CTIME(3) |
Linux Programmer's Manual CTIME(3) |
NAME |
|
asctime, ctime, gmtime, localtime, mktime - transform binary date and time to ASCII |
|
SYNOPSIS |
|
|
#include <time.h> char "asctimefconst struct tm *timeptr); char *ctime(const timej *timep); struct tm "gmtime(const timej *timep); struct tm *localtime( const timej *timep); timej mktime(struct tm *timeptr); extern char *tzname[2]; long int timezone; extern int daylight; |
DESCRIPTION |
|
|
The ctime(), gmtimef) and localtime() functions all take an argument of data type timej which represents calendar time. When interpreted as an absolute time value, it rep-resents the number of seconds elapsed since 00:00:00 on January 1,1970, Coordinated Universal Time (UTC). The ctime() function converts the calendar time timep into a string of the form "Wed Jun 30 21:49:08 1993\n" The abbreviations for the days of the week are Sun, Mon, Tue, Wed, Thu, Fri, and Sat. The abbre-viations for the months are Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, and Dec. The return value points to a statically allocated string which might be overwritten by subsequent calls to any of the date and time functions. The function also |
Вот это то, что нам необходимо. Мы имеем значение time_t в записях utmp. А нам требуется строка в формате, подобном такому:
Jun 30 21:49
Функция ctime(3) выбирает указатель на time_t, а при окончании возвращает указатель на! строку, которая будет выглядеть примерно так:
Wed Jun 30 21:49:08 1993\n
ЛЛЛЛЛЛЛЛЛЛЛЛ
Заметим, что строка, которая нам нужна для работы who, вставляется в строку возврата функции ctime. Это позволяет достаточно просто производить кодировку даты для who. Мы обращаемся к функции ctime и получаем после ее работы строку из 12 символов со смещением, равным 4. Это выполняется при выполнении оператора printf("%12.12s", ctime(&t) +4).
Одновременный вывод всего сразу
Теперь мы знаем, как подавить пустые записи, и знаем, как отобразить значение utjime в читабельном виде. Далее представлена окончательная версия программы who2.c:
/* who2.c |
- читает файл /etc/utmp и выводит список информации из него |
*
|
- подавляет пустые записи |
*/
|
- правильно форматирует время |
#include <stdio.h>
#include <unistd.h>
#include <utmp.h>
#include <fcntl.h>
#include <time.h>
/* «define SHOWHOST*/
void showtime(long);
void showjnfofstruct utmp *);
int main()
{
struct utmp utbuf; Г сюда читается информация */
int utmpfd; /* чтение происходит из этого дескриптора */
if((utmpfd = open(UTMP FILE, О RDONLY)) == -1){
perror(UTMP.FILE);
exit(1);
}
while(read(utmpfd, &utbuf, sizeof(utbuf)) == sizeof(utbuf))
show_info(&utbuf);
close( utmpfd);
return 0;
}
/*
* show info()
* отображает содежимое структуры utmp
* в удобном для восприятия виде
* * ничего не отображает, если в записи нет имени пользователя*/ void show_info(struct utmp *utbufp)
{ \
if (utbufp->ut_type != USER_PROCESS)
return;
printf("%-8.8s", utbufp->ut_name); /* входное имя */
printf(""); I* пробел */
printf("%-8.8s", utbufp->ut_line); /* терминал */
printf(""); /* пробел */
showtime(utbufp->ut_time); /* отображение времени 7
#ifdefSHOWHOST
if(utbufp->ut_host[0] !='\0')
printff' (%s)", utbufp->ut_host); /* хост */
#endif
printf("\n"); /* перевод на новую строку */
}
void showtimeflong timeval)
/*
* отображает времяз^формате, удобном для восприятия
* использует функцию ctime для формирования строки с изображением времени
* Замечание: посредством формата %12.12s выводится строка из 12 символов,
* при значении LIMITS, равно 12 символов.
*/
{
char *ср; /* адрес со значением времени */
ср = ctime(&timeval); /* преобразование значения времени в строку */
/* строка должна иметь приблизительно такой вид */
/* Моn Feb 4 00:46:40 EST 1991 */
/* 0123456789012345. */
printf("%12.12s", cp+4); /* вывести 12 символов с позиции 4 */
}
Тестирование программы who2.c
Откомпилируем и запустим на исполнение программу who2.c. Для разнообразия выключим настройку SHOWHOST. Далее запустим на исполнение системную версию команды who и сравним полученные результаты:
$ccwho2.c –о iwho2 $who2 |
||
rlscott |
ttyp2 |
Jul 23 01:07 |
acotton |
ttyp3 |
Jul 22 22:24 |
spradlin |
ttyp5 |
Jul 17 20:51 |
spradlin |
ttyp7 |
Jul 19 22:04 |
king |
ttyp8 |
Jul 22 11:32 |
berschba |
ttyp9 |
Jul 21 10:05 |
rserved |
ttypa |
Jul 1321:29 |
Rserved |
ttypd |
Jul 13 21:31 |
molay |
ttyqO |
Jul 22 20:03 |
cweiner |
ttyq8 |
Jul 21 16:40 |
mnabavi |
ttyx2 |
Apr 10 23:11 |
$who |
||
rlscott |
ttyp2 |
Jul 23 01:07 |
acotton |
ttyp3 |
Jul 22 22:24 |
spradlin |
ttyp5 |
Jul 17 20:51 |
spradiin |
ttyp7 |
Jul 19 22:04 |
king |
ttyp8 |
Jul 22 11:32 |
berschba |
ttyp9 |
Jul 21 10:05 |
rserved |
ttypa |
Jul 13 21:29 |
rserved |
ttypd |
Jul 13 21:31 |
molay |
ttyqO |
Jul 22 20:03 |
Cweiner |
ttyq8 |
Jul 21 16:40 |
mnabavi |
ttyx2 |
Apr 10 23:11 |
$
Есть некоторое отличие в форматировании результатов. В различных версиях команды who используются различные по ширине колонки при выводе результатов. При изменении размеров колонок протокола вывода мы можем в точности добиться совпадения формата вывода по отношению к стандартному варианту. Можете заняться этим на своей системе.
В некоторых версиях команды who производится вывод имени хоста для удаленной системы, если такая система была зафиксирована. В других же версиях такое имя не выводится. Программа выдает точный список пользователей, имена их терминальных линий и времена вхождения пользователей в систему.
2.5.6. Взгляд назад и взгляд вперед
Мы начали эту главу с постановки простого вопроса: ''Как работает в Unix команда who?" Мы следовали в тексте трем сформулированным шагам. Во-первых, мы изучили, что делает команда. Затем мы разобрались^гюсредством детального изучения технической документации, как работает команда. Далее написали собственную версию программы, чтобы убедиться в том, что мы действительно понимаем, как работает команда.
По мере нахождения решений на каждом из трех шагов мы научились использовать электронный справочник Unix и заголовочные файлы. Написание собственной версии программы привело к закреплению рассмотренного материала. Стала ясной структура файла utmp. Мы убедились в том, что каждое вхождение в систему приводит к появлению записи в журнале. Мы изучили, каким образом в Unix представляются временные величины. Это будет полезно при работе с другими частями Unix.
Наконец, мы почитали документацию по надлежащим темам. На страницах справочника для файла utmp были найдены ссылки на файл wtmp. А со страниц справочника для функции ctime есть ссылки на другие функции, которые связаны со временем. Эти ссылки дают дополнительное представление о структуре системы.