1. Рассмотрим программу, приведенную на Рисунке 5.35. Какое значение возвращает каждая операция read и что при этом содержится в буфере? Опишите, что происходит в ядре во время выполнения каждого вызова read.
2. Вновь вернемся к программе на Рисунке 5.35 и предположим, что оператор lseek(fd,9000L,0); стоит перед первым обращением к функции read. Что ищет процесс и что при этом происходит в ядре?
3. Процесс может открыть файл для работы в режиме добавления записей в конец файла, при этом имеется в виду, что каждая операция записи располагает данные по адресу смещения, указывающего текущий конец файла. Таким образом, два процесса могут открыть файл для работы в режиме добавления записей в конец файла и вводить данные, не опасаясь затереть записи друг другу. Что произойдет, если процесс откроет файл в режиме добавления в конец, а записывающую головку установит на начало файла?
4. Библиотека стандартных подпрограмм ввода-вывода повышает эффективность выполнения пользователем операций чтения и записи благодаря буферизации данных в библиотеке и сохранению большого количества модулей обращения к операционной системе, необходимых пользователю. Как бы вы реализовали библиотечные функции fread и fwrite? Что должны делать библиотечные функции fopen и fclose?
#include <fcntl.h> main() { int fd; char buf[1024]; fd = creat("junk",0666); lseek(fd,2000L,2); /* ищется байт с номером 2000 */ write(fd,"hello",5); close(fd); fd = open("junk",O_RDONLY); read(fd,buf,1024); /* читает нули */ read(fd,buf,1024); /* считывает нечто, отличное от 0 */ read(fd,buf,1024); } |
5. Если процесс читает данные из файла последовательно, ядро запоминает значение блока, прочитанного с продвижением, в индексе, хранящемся в памяти. Что произойдет, если несколько процессов будут одновременно вести последовательное считывание данных из одного и того же файла?
6. Рассмотрим программу, приведенную на Рисунке 5.36. Что произойдет в результате выполнения программы? Обоснуйте ответ. Что произошло бы, если бы объявление массива buf было вставлено между объявлениями двух других массивов размером 1024 элемента каждый? Каким образом ядро устанавливает, что прочитанная порция данных слишком велика для буфера?
*7. В файловой системе BSD разрешается фрагментировать последний блок файла в соответствии со следующими правилами:
Разработайте алгоритм, присоединяющий к файлу фрагменты блока. Какие изменения должны быть сделаны в индексе, чтобы позволить использование фрагментов? Какие преимущества с системной точки зрения предоставляет использование фрагментов для тех файлов, которые используют блоки косвенной адресации? Не выгоднее ли было бы назначать фрагменты во время выполнения функции close вместо того, чтобы назначать их при выполнении функции write?
*8. Вернемся к обсуждению, начатому в главе 4 и касающемуся расположения данных в индексе файла. Для того случая, когда индекс имеет размер дискового блока, разработайте алгоритм, по которому остаток данных файла переписывается в индексный блок, если помещается туда. Сравните этот метод с методом, предложенным для решения предыдущей проблемы.
*9. В версии V системы функция fcntl используется для реализации механизма захвата файла и записи и имеет следующий формат: fcntl(fd,cmd,arg); где fd - дескриптор файла, cmd - тип блокирующей операции, а в arg указываются различные параметры, такие как тип блокировки (записи или чтения) и смещения в байтах (см. приложение). К блокирующим операциям относятся
Ядро автоматически снимает блокировки, установленные процессом, при закрытии файла. Опишите работу алгоритма, реализующего захват файла и записи. Если блокировки являются обязательными, другим процессам следует запретить доступ к файлу. Какие изменения следует сделать в операциях чтения и записи?
*10. Если процесс приостановил свою работу в ожидании снятия с файла блокировки, возникает опасность взаимной блокировки: процесс A может заблокировать файл "one" и попытаться заблокировать файл "two", а процесс B может заблокировать файл "two" и попытаться заблокировать файл "one". Оба процесса перейдут в состояние, при котором они не смогут продолжить свою работу. Расширьте алгоритм решения предыдущей проблемы таким образом, чтобы ядро могло обнаруживать ситуации взаимной блокировки и прерывать выполнение системных функций. Следует ли поручать обнаружение взаимных блокировок ядру?
11. До существования специальной системной функции захвата файла пользователям приходилось прибегать к услугам параллельно действующих процессов для реализации механизма захвата путем вызова системных функций, выполняющих элементарные действия. Какие из системных функций, описанных в этой главе, могли бы использоваться? Какие опасности подстерегают при использовании этих методов?
12. Ричи заявлял (см. [Ritchie 81]), что захвата файла недостаточно для того, чтобы предотвратить путаницу, вызываемую такими программами, как редакторы, которые создают копию файла при редактировании и переписывают первоначальный файл по окончании работы. Объясните, что он имел в виду, и прокомментируйте.
13. Рассмотрим еще один способ блокировки файлов, предотвращающий разрушительные последствия корректировки. Предположим, что в индексе содержится новая установка прав доступа, позволяющая только одному процессу в текущий момент открывать файл для записи и нескольким процессам открывать файл для чтения. Опишите реализацию этого способа.
*14. Рассмотрим программу (Рисунок 5.37), которая создает каталог с неверным форматом (в каталоге отсутствуют записи с именами "." и ".."). Попробуйте, находясь в этом каталоге, выполнить несколько команд, таких как ls -l, ls -ld, или cd. Что произойдет при этом?
15. Напишите программу, которая выводит для файлов, имена которых указаны в качестве параметров, информацию о владельце, типе файла, правах доступа и времени доступа. Если файл (параметр) является каталогом, программа должна читать записи из каталога и выводить вышеуказанную информацию для всех файлов в каталоге.
16. Предположим, что у пользователя есть разрешение на чтение из каталога, но нет разрешения на исполнение. Что произойдет, если каталог использовать в качестве параметра команды ls, заданной с опцией "-i"? Что будет, если указана опция "-l"? Поясните свои ответы. Ответьте на вопрос, сформулированный для случая, когда есть разрешение на исполнение, но нет разрешения на чтение из каталога.
17. Сравните права доступа, которые должны быть у процесса для выполнения следующих действий, и прокомментируйте:
*18. Напишите программу, которая навещает все каталоги, начиная с текущего. Как она должна управлять циклами в иерархии каталогов?
19. Выполните программу, приведенную на Рисунке 5.38, и объясните, что при этом происходит в ядре. (Намек: выполните команду pwd, когда программа закончится).
20. Напишите программу, которая заменяет корневой каталог указанным каталогом, и исследуйте дерево каталогов, доступное для этой программы.
21. Почему процесс не может отменить предыдущий вызов функции chroot? Измените конкретную реализацию процесса таким образом, чтобы он мог менять текущее значение корня на предыдущее. Какие у этой возможности преимущества и неудобства?
22. Рассмотрим простой пример канала (Рисунок 5.19), когда процесс записывает в канал строку "hello" и затем считывает ее. Что произошло бы, если бы массив для записи данных в канал имел размер 1024 байта вместо 6 (а объем считываемых за одну операцию данных оставался равным 6)? Что произойдет, если порядок вызова функций read и write в программе изменить, поменяв функции местами?
23. Что произойдет при выполнении программы, иллюстрирующей использование поименованных каналов (Рисунок 5.19), если функция mknod обнаружит, что канал с таким именем уже существует? Как этот момент реализуется ядром? Что произошло бы, если бы вместо подразумеваемых в тексте программы одного считывающего и одного записывающего процессов связь между собой через канал попытались установить несколько считывающих и записывающих процессов? Как в этом случае гарантировалась бы связь одного считывающего процесса с одним записывающим процессом?
24. Открывая поименованный канал для чтения, процесс приостанавливается до тех пор, пока еще один процесс не откроет канал для записи. Почему? Не мог бы процесс успешно пройти функцию open, продолжить работу до того момента, когда им будет предпринята попытка чтения данных из канала, и приостановиться при выполнении функции read?
25. Как бы вы реализовали алгоритм выполнения системной функции dup2 (из версии 7), вызываемой следующим образом: dup2(oldfd,newfd); где oldfd - файловый дескриптор, который дублируется дескриптором newfd? Что произошло бы, если бы дескриптор newfd уже принадлежал открытому файлу?
*26. Какие последствия имело бы решение ядра позволить двум процессам одновременно смонтировать одну и ту же файловую систему в двух точках монтирования?
27. Предположим, что один процесс меняет свой текущий каталог на каталог "/mnt/a/b/c", после чего другой процесс в каталоге "/mnt" монтирует файловую систему. Завершится ли функция mount успешно? Что произойдет, если первый процесс выполнит команду pwd? Ядро не позволит функции mount успешно завершиться, если значение счетчика ссылок в индексе каталога "/mnt" превышает 1. Прокомментируйте этот момент.
28. При исполнении алгоритма пересечения точки монтирования по имени ".." в маршруте поиска файла ядро проверяет выполнение трех условий, связанных с точкой монтирования: что номер обнаруженного индекса совпадает с номером корневого индекса, что рабочий индекс является корнем файловой системы и что имя компоненты маршрута поиска - "..". Почему необходимо проверять выполнение всех трех условий? Докажите, что проверки любых двух условий недостаточно для того, чтобы разрешить процессу пересечь точку монтирования.
29. Если пользователь монтирует файловую систему только для чтения, ядро устанавливает соответствующий флаг в суперблоке. Как ядро может воспрепятствовать выполнению операций записи в функциях write, creat, link, unlink, chown и chmod? Какого рода информацию записывают в файловую систему все перечисленные функции?
*30. Предположим, что один процесс пытается демонтировать файловую систему, в то время как другой процесс пытается создать в файловой системе новый файл. Только одна из функций umount и creat выполнится успешно. Подробно рассмотрите возникшую конкуренцию.
*31. Когда функция umount проверяет отсутствие в файловой системе активных файлов, возникает одна проблема, связанная с тем, что корневой индекс файловой системы, назначаемый при выполнении функции mount с помощью алгоритма iget, имеет счетчик ссылок с положительным значением. Как функция umount сможет убедиться в отсутствии активных файлов и отчитаться перед корнем файловой системы? Рассмотрите два случая:
32. Обратите внимание на то, что при выполнении команды ls -ld количество связей с каталогом никогда не равно 1. Почему?
33. Как работает команда mkdir (создать новый каталог)? (Наводящий вопрос: какие номера по завершении выполнения команды имеют индексы для файлов "." и ".."?)
*34. Понятие "символические связи" имеет отношение к возможности указания с помощью функции link связей между файлами, принадлежащими к различным файловым системам. С файлом символической связи ассоциирован указатель нового типа; содержимым файла является имя пути поиска того файла, с которым он связан. Опишите реализацию символических связей.
*35. Что произойдет, если процесс вызовет функцию unlink("."); Каким будет текущий каталог процесса? Предполагается, что процесс обладает правами суперпользователя.
36. Разработайте системную функцию, которая усекает существующий файл до произвольных размеров, указанных в качестве аргумента, и опишите ее работу. Реализуйте системную функцию, которая позволяла бы пользователю удалять сегмент файла, расположенный между двумя адресами, заданными в виде смещений, и сжимать файл. Напишите программу, которая не вызывала бы эти функции, но обладала бы теми же функциональными возможностями.
37. Опишите все условия, при которых счетчик ссылок в индексе может превышать значение 1.
38. Затрагивая тему абстрактных обращений к файловым системам, ответьте на вопрос: следует ли файловой системе каждого типа иметь личную операцию блокирования, вызываемую из общей программы, или же достаточно общей операции блокирования?
Рисунок 5.36. Чтение большой порции данных в маленький буфер
#include <fcntl.h>
main()
{
int fd;
char buf[256];
fd = open("/etc/passwd",O_RDONLY);
if (read(fd,buf,1024) < 0)
printf("чтение завершается неудачно\n");
}
Рисунок 5.37. Каталог, создание которого не завершено
main(argc,argv)
int argc;
char *argv[];
{
if (argc != 2)
{
printf("введите: команда имя каталога\n");
exit();
}
/* права доступа к каталогу: запись, чтение и ис-
полнение разрешены для всех */
/* только суперпользователь может делать следую-
щее */
if (mknod(argv[1],040777,0) == -1)
printf("mknod завершилась неудачно\n");
}
Рисунок 5.38. Пример программы с использованием функции chdir
main(argc,argv)
int argc;
char *argv[];
{
if (argc != 2)
{
printf("нужен 1 аргумент - имя каталога\n");
exit();
}
if (chdir(argv[1]) == -1)
printf("%s файл не является каталогом\n",argv[1]);
}