[P&AM Lab] Пример для разбора исходного кода - cat(1) из BSD 4.2
Grigoriy A. Sitkarev
sitkarev на komitex.ru
Чт Дек 9 22:44:07 MSK 2010
Сейчас по порядку.
Борис Липин пишет:
> Тут парочка вопросов возникла по поводу cat
> 1)
> switch(argv[1][1]) {
> case 0:
> break;
> case 'u':
> setbuf(stdout, (char *)NULL);
> uflg++;
> continue;
> case 'n':
> nflg++;
> continue;
> ...}
> Почему здесь мы для флагов используем ++ а не просто приравниваем флагк 1( uflg=1); Связанно ли это както с тем что операция инкремента вассемблере занимает меньше памяти чем mov?
Меньше, да и традиция такая скорее. Дело в том что пользователь может
несколько раз указать ту же опцию -u. Здесь ведётся подсчёт, сколько раз
он её указал, получается так. Никто не запрещает там сделать uflg = 1 и
алгоритмически это будет верно, но традиция есть традиция.
> 2)
> Что содержится в поле st_ino структуры struct stat statb,всмысле какойу неё логический смысл, и зачем мы её вводили в программу?
Это очень важная структура, в ней хранится метаинформация о файле.
Системный вызов stat(2) её туда помещает. В программе она используется
для того чтобы:
1) проверить что файл не является файлом устройства (блочного или
символьного). Проверяются биты S_IFBLK и S_IFCHR в поле .st_mode.
2) узнать размер блока т.е. дискретной единицы с которой ведутся
операции чтения и записи. Это поле .st_blksize.
3) проверяется что файл, который выводят, и файл в который выводят
являются разными файлами. В Unix файл уникально идентифицируется двумя
целыми числами - номером устройства и номером индексного дескриптора.
Эти поля также содержатся в структуре stat (.st_dev, st_ino). Вы помните
что в Unix есть файлы-ссылки, т.е. разные имена могут ссылаться на одни
и те же данные файла. Чтобы cat(1) не напортачил, таким образом
проверяется что файлы "что выводить" и "куда выводить" - разные.
Рекомендую прочесть man-страницу stat(2).
> 3)
> stdout-ссылка на стандартный файл вывода.А на что именно он ссылается?
Есть соглашение в Unix программах (и те которые на Си в системах общего
применения) при запуске открыты три файла с номерами дескрипторов файлов
(определены в unistd.h):
STDIN_FILENO - 0
STDOUT_FILENO - 1
STDERR_FILENO - 2
Можно пользоваться как макросами, так и прямо числовыми константами.
Если мы пишем "параноидальную" программу, то неплохо бы проверить что
эти дескрипторы действительно есть, иначе перенаправлять их в /dev/null.
Считается что у любой Си программы эти файлы открыты и должны быть
соединены с соответствующими файлами (для интерактивных программ это как
правило текущее устройство терминала пользователя). В Linux например
можно у программы посмотреть с какими файлами эти дескрипторы
ассоциированы, пролистав длинным листингом (ls -la) каталог
/proc/<PID>/fd. Получается что открывать самому эти файлы не нужно, как
обычно надо делать с другими файлами, через системный вызов open(2),
потому что они должны быть открыты уже.
Это дескрипторы "сырого" (небуферизованного) ввода/вывода, т.е. которые
мы пользуем с вызовами read(2), write(2) и т.д. Вы прямо с shell можете
перенаправлять у программы эти дескрипторы, если помните, мы это делали
на занятиях, я показывал там что-то вроде:
$ cat /etc/passwd > /root/saved_passwd
Есть три потока буферизованного ввода/вывода, это то что вы можете
использовать с fread(3), fwrite(3), fprintf(3) и т.д.:
stdin
stdout
stderr
Они объявлены где-то в заголовках как:
extern FILE *stdin, *stdout, *stderr;
И в каждой Си программе они есть в адресном пространстве. Они также
соединены со стандартным вводом, выводом и выводом ошибок. Поэтому я
могу печатать сообщения об ошибках так:
fprintf(stderr, "error: %s:%d:%s(): something is wrong!\n",
__FILE__, __LINE__, __FUNCTION__);
__FILE__, __LINE__, и __FUNCTION__ - это макросы, которые препроцессор
раскрывает в имена файла, номера строки и имя функции.
Вызов
printf("Message");
эквивалентен:
fprintf(stdout, "Message");
Успехов.
--
Г.А.
Подробная информация о списке рассылки Lab