Андрей Робачевский - Операционная система UNIX
Символическая связь позволяет косвенно адресовать другой файл файловой системы. Системный вызов symlink(2) служит для создания символической связи. Этим вызовом, кстати, пользуется команда ln -s.
#include <unistd.h>
int symlink (const char *name, const char *synmame);
После создания символической связи, доступ к целевому файлу name может осуществляться с помощью symname. При этом, функция open(2), принимая в качестве аргумента имя символической связи, на самом деле открывает целевой файл. Такая особенность называется следованием символической связи. Не все системные вызовы обладают этим свойством. Например, системный вызов unlink(2), удаляющий запись в каталоге, действует только на саму символическую связь. В противном случае, мы не имели бы возможности удалить ее. В табл. 2.11 показано, как работают с символическими связями различные системные вызовы.
Таблица 2.11. Интерпретация символической связи различными системными вызовами
Системный вызов Следует символической связи Не следует символической связи access(2) + chdir(2) + chmod(2) + chown(2) + lchown(2) + creat(2) + exec(2) + link(2) + mkdir(2) + mknod(2) + open(2) + readlink(2) + rename(2) + stat(2) + lstat(2) + unlink(2) +Для чтения содержимого файла — символической связи используется системный вызов readlink(2):
#include <unistd.h>
int readlink(const char *path, void *buf, size_t bufsiz);
Аргумент path содержит имя символической связи. В буфере buf размером bufsiz возвращается содержимое файла — символической связи.
Для иллюстрации к вышеприведенным рассуждениям приведем пример программы, которая сначала выводит содержимое символической связи, а затем — целевого файла, пользуясь в обоих случаях символическим именем:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define BUFSZ 256
/* В качестве аргумента программа принимает имя
символической связи */
main(int argc, char *argv[]) {
char buf[BUFSZ+1];
int nread, fd;
/* Прочитаем содержимое самой символической связи */
printf("Читаем символическую связьn");
nread = readlink(argv[1], buf, BUFSZ);
if (nread < 0) {
perror("readlink");
exit(1);
}
/* readlink не завершает строку ' ' */
printf("Символическая связь:n %sn", buf);
/* Теперь прочитаем содержимое целевого файла */
printf("Читаем целевой файлn");
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
exit(2);
}
nread = read(fd, buf, BUFSIZ);
if (nread < 0) {
perror("read");
exit(3);
}
buf[nread] = ' ';
printf("Целевой файл:n %sn", buf);
close(fd);
exit(0);
}
Перед тем как запустить программу, создадим символическую связь с файлом unix0.txt:
$ ln -s unix0.txt symlink.txt
$ ls -l
lrwxrwxrwx 1 andy user 10 Jan 6 09:54 symlink.txt -> unix0.txt
-rw-r--r-- 1 andy user 498 Jan 6 09:53 unix0.txt
$ a.out symlink.txt
Читаем символическую связь
Символическая связь:
unix0.txt
Читаем целевой файл
Целевой файл:
Начиная с 1975 года фирма AT&T начала предоставлять лицензии на
использование операционной системы как научно-образовательным
учреждениям, так и коммерческим организациям. Поскольку основная
часть системы поставлялась в исходных текстах, написанных на
языке С, опытным программистам не требовалось детальной
документации, чтобы разобраться в архитектуре UNIX. С ростом
популярности микропроцессоров
...
Файлы, отображаемые в памяти
Системный вызов mmap(2) предоставляет механизм доступа к файлам, альтернативный вызовам read(2) и write(2). С помощью этого вызова процесс имеет возможность отобразить участки файла в собственное адресное пространство. После этого данные файла могут быть получены или записаны путем чтения или записи в память. Функция mmap(2) определяется следующим образом:
#include <sys/types.h>
#include <sys/mman.h>
caddr_t mmap(caddr_t addr, size_t len, int prot,
int flags, int fildes, off_t off);
Этот вызов задает отображение len байтов файла с дескриптором fildes, начиная со смещения off, в область памяти со стартовым адресом addr. Разумеется, перед вызовом mmap(2) файл должен быть открыт с помощью функции open(2). Аргумент prot определяет права доступа к области памяти, которые должны соответствовать правам доступа к файлу, указанным в системном вызове open(2). В табл. 2.12 приведены возможные значения аргумента prot и соответствующие им права доступа к файлу. Возможно логическое объединение отдельных значений prot. Так значение PROT_READ | PROT_WRITE соответствует доступу O_RDWR к файлу.
Таблица 2.12. Права доступа к области памяти
Значение аргумента prot Описание Права доступа к файлу PROT_READ Область доступна для чтения r PROT_WRITE Область доступна для записи w PROT_EXEC Область доступна для исполнения x PROT_NONE Область недоступна -Обычно значение addr задается равным 0, что позволяет операционной системе самостоятельно выбрать виртуальный адрес начала области отображения. В любом случае, при успешном завершении возвращаемое системным вызовом значение определяет действительное расположение области памяти.
Операционная система округляет значение len до следующей страницы виртуальной памяти.[19] Например, если размер файла 96 байтов, а размер страницы 4 Кбайт, то система все равно выделит область памяти размером 4096 байтов. При этом 96 байтов займут собственно данные файла, а остальные 4000 байтов будут заполнены нулями. Процесс может модифицировать и оставшиеся 4000 байтов, но эти изменения не отразятся на содержимом файла. При обращении к участку памяти, лежащему за пределами файла, ядро отправит процессу сигнал SIGBUS[20]. Несмотря на то что область памяти может превышать фактический размер файла, процесс не имеет возможности изменить его размер.
Использование права на исполнение (prot = PROT_EXEC) позволяет процессу определить собственный механизм загрузки кода. В частности, такой подход используется редактором динамических связей при загрузке динамических библиотек, когда библиотека отображается в адресное пространство процесса. Значение PROT_NONE позволяет приложению определить собственные механизмы контроля доступа к разделяемым объектам (например, к разделяемой памяти), разрешая или запрещая доступ к области памяти.