
- •Кафедра программного обеспечения информационных технологий
- •«Операционные системы и системное программирование»
- •40 01 01
- •Содержание
- •Введение
- •Разработка программ в ос unix
- •1.1 Отличительные черты ос unix
- •1.2 Основы архитектуры операционной системы unix
- •1.3 Ядро системы
- •1.4 Пользователи системы, атрибуты пользователя
- •1.5 Системные вызовы и функции стандартных библиотек
- •1.6 Описание программы, переменные окружения
- •1.7 Аргументы и опции программы
- •1.8 Обработка ошибок
- •2 Файлы и файловая система
- •2.1 Файлы
- •2.2 Типы файлов
- •2.2.1 Обычные файлы
- •2.2.2 Каталоги
- •2.2.3 Файлы символичной связи (ссылки)
- •2.2.4 Файлы устройства
- •2.2.5 Именованные каналы
- •2.2.6 Сокеты
- •2.3 Владельцы файлов и права доступа к файлу
- •2.4 Дополнительные атрибуты файла
- •2.5 Файловый ввод/вывод
- •Открытие файла
- •2.6 Мультиплексированный ввод/вывод
- •2.7 Векторный ввод/вывод
- •2.8 Файлы, отображающиеся в памяти
- •2.9 Каталоги, работа с каталогами
- •2.9.1 Создание каталога
- •2.9.2 Удаление каталога
- •2.9.3 Чтение информации из каталога
- •2.9.4 Закрытие каталога
- •2.10 Создание жестких ссылок
- •2.11 Символическая ссылка
- •2.12 Удаление ссылки (или имени файла)
- •2.13 Переименование файла
- •2.14 Файловая система ос unix
- •2.14.1 Организация файловой системы ext2
- •2.15 Файлы устройств
- •3 Процессы
- •3.1 Виды процессов
- •3.2 Создание процесса
- •3.3 Вызовы семейства exec
- •3.4 Функции завершения процесса
- •3.5 Ошибки
- •3.6 Копирование при записи
- •3.7 Системные вызовы ожидания завершения процесса
- •3.8 Системный вызов system
- •3.9 Основные параметры, передаваемые процессу
- •3.10 Сеансы и группы процессов
- •4 Взаимодействие процессов
- •4.1 Сигналы
- •4.1.1 Отправка (генерация) сигнала
- •4.1.2 Наборы сигналов
- •4.1.3 Блокировка сигналов
- •4.2 Неименнованные каналы (трубы)
- •4.2.1 Размер канала и взоимодействие процессов при передаче данных
- •4.3 Именнованные каналы
- •4.4 Дополнительные средства межпроцессного взоимодействия
- •4.5 Механизмы межпроцессорного взаимодействия
- •4.5.1 Очереди сообщений
- •4.5.2 Семафоры Семафоры как теоретическая конструкция
- •4.5.3 Разделяемая память
- •4.5.4 Потоки
- •Int pthread_setschedparam(pthread_t tid, int policy, const struct sched_param *param);
- •Int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param);
- •5 Операционные системы
- •5.1 Понятие операционной системы
- •5.2 Характеристики современных ос
- •5.2.1 Многопоточность
- •5.2.2 Распределенные ос
- •5.2.3 Концепция ос на основе микроядра
- •5.2.4 Функции микроядра.
- •5.3 Принципы построения ос
- •5.4 Концептуальные основы ос
- •5.4.1 Процессы
- •Модель работы процесса с двумя приостановочными состояниями
- •Варианты решения:
- •Решение задачи взаимного исключения. Алгоритм Деккера.
- •Решение задачи взаимного исключения. Алгоритм Пэтерсона..
- •Синхронизирующие примитивы (семафоры).
- •Задача “производитель-потребитель” Общие семафоры
- •Задача “производитель-потребитель”, буфер неограниченного размера(Спящий парикмахер)
- •Задача “производитель-потребитель”, буфер ограниченного размера
- •5.4.2 Распределение ресурсов. Проблема тупиков
- •Алгоритм банкира
- •Применение алгоритма банкира
- •5.4.3 Монитороподобные средства синхронизации
- •Механизм типа «критическая область»
- •5.4.4 Виртуализация
- •5.4.5 Подсистема управления памятью
- •5.4.6 Виртуальная оперативная память
- •5.5 Аппаратные особенности процессоров Intel-архитектуры, направленных на поддержку многозадачности
- •5.5.1 Сегментация памяти. Ia-32
- •5.5.2 Распределение памяти в реальном режиме
- •5.5.3 Организация защиты в процессоре
- •5.5.4 Поддержка многозадачности в процессорах архитектуры ia-32
2.7 Векторный ввод/вывод
Векторный ввод/вывод – это способ ввода/вывода, когда системный вызов записывает или считывает данные из потока данных в вектор буферов.
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int count);
ssize_t writev(int fd, const struct iovec *iov, int count);
Первый системный вызов читает, а второй – записывает указанное число данных, находящееся в буферах, определенных вторым параметром в дескриптор первого параметра. В качестве третьего параметра используется количество сегментов, которое считывается или записывается (0<count<IOV_MAX=1024).
Каждый сегмент имеет такую структуру: первый параметр указывает на буфер, второй – на размер (количество байтов для обмена).
struct iovec
{ void *iov_base;
size_t iov_len; }
Пример использования векторного ввода/вывода:
#include <stdio.h>
#include <sys/types.h>
#include <fentf.h>
#include <string.h>
#include <sys/uio.h>
int main()
{
struct iovec iov[3];
ssize_t nr;
int fd;
char *buf[]={“AAAAAAAAA\n”, “BBBBB\n”, “CCCCCCC\n”};
fd=open(“fil1.txt”, O_WRONLY|O_CREAT|O_TRUNC);
if (fd==-1)
{ perror(“open”);
return(1); }
for (i=0; i<3; i++)
{ iov[i].iov_base=buf[i];
iov[i].iov_len=strlen(buf[i]); }
nr=writev(fd,iov,3);
if (nr==-1)
{ perror(“writev”);
return(1); }
printf(“Записаны %d байт \n”, nr);
if (close(fd))
{ perror(“close”);
return(1); }
}
Производится запись в файл трех строк массива. Для этого сначала открывается файл и проверяется, чтобы не было ошибок. Формируются сегменты данных (в первом поле указывается адрес, во втором – количество байт). Далее системный вызов writev записывает в буфера (указанный адрес) то количество, которое надо записать. Анализ возвращаемых значений проводится с помощью числа записанных байт (nr).
Применение функции векторного ввода/вывода предназначено для повышения производительности операций обмены за счет внутренней системной оптимизации выполняемых действий. Помимо этого первый оператор обмена может заменить несколько операторов ввода/вывода.
2.8 Файлы, отображающиеся в памяти
Альтернатива стандартному вводу/выводу является интерфейс представленный ядром ОС, который позволяет приложению отображать файл в память, то есть создавать соответствие один к одному между адресом в памяти и словом в файле. За счет такого механизма программа может обращаться файлу через память как к любым другим данным, находящимся в памяти.
Имеется механизм прозрачного отображения действий по отношению к данным в области памяти не данные, находящиеся в файле на диске:
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
Этот системный вызов запрашивает у ядра ОС разрешение на отображение этих len байтов из файла fd, начиная со смещения в файле offset. Разрешение на доступ отображает параметр prot, а допустимые возможности определяет flags.
Далее представлены возможные значения prot:
PROT_READ, страница доступна для считывания
PROT_WRITE, страница доступна для записи
PROT_EXEC, страница доступна для выполнения
PROT_NONE, доступ к странице запрещен
Допустимые варианты flags:
MAP_FIXED, позволяет считать первый параметр, а если ядро не может разместить данные по указанному адресу, то выводит сообщение об ошибке
MAP_PRIVATE, отображение приватно для файла по отношению к процессу, изменения не видны для других процессов и не повторяются в физическом файле
MAP_SHAKE, действие видны для остальных процессов и отображаются на физических файлах
Первый же параметр addr служит для того, чтобы определять место в ОП, куда лучше всего отображать данные в файл. Если в качестве данного параметра представлено значение NULL (или 0), то ОС сама выбирает, куда отображать данные.
void *p;
. . .
p=mmap(0,len,PROT_READ,MAP_SHADER,fd,0);
Возвращает системный вызов фактический адрес в памяти, куда будет производиться отображение.
Отображение ведется по страницам. Страница – это наименьшая единица памяти, которая может обладать собственным разрешением и поведением. Отображения области памяти должны быть кратны размеру страницы, и оба параметра addr и offset должны быть выровнены на границе страницы.
Если параметр len не кратен страницам, для отображения формы области памяти с числом страниц, которое обеспечивает разрешение больше len, но ближнее по числу страниц. Но при этом пользовательский процесс не может выходить за границу len.
Для того, чтобы узнать размер страницы:
#include <unistd.h>
long sysconf(int name);
long page_size=sysconf(_SC_PAGESIZE);
В случае успешного завершения функции mmap возвращается адрес отображения в памяти, а в случае ошибки значение MAP_FAILED и устанавливает значения errno:
EACCESS – файл, указанный при помощи файлового дескриптора fd, не является обычным файлом или же режим, в котором открыт файл, конфликтует со значением prot или flags
EAGAIN – файл заблокирован
EBADF – значение fd не является допустимым
EINVAL – один или несколько параметров имеют недопустимое значение
ENFILE – достигнуто максимальное количество открытых файлов
ENODEV – используемая файловая система не поддерживает отображение в память
ENOMEM – у процесса недостаточно памяти
EOVERFLOW – выход за пределы адресного пространства
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fentl.h>
#include <unistd.h>
#include <sys/mman.h>
int main(int argc,char *argv[]) {
struct stat sb;
off_t len;
char *p;
int fd;
if (argc<2) //проверяет корректность вызова
{ printf(“Ошибка вызова программы \n”);
return(1); }
fd=open(argv[1],O_RDONLY); //открытие файла
if (fd==-1)
{ perror(“open”);
return(1); }
if (fstat(fd,&sb)==-1)
{ perror(“fstat”);
return(1); }
if (!S_ISREG(sb.st_mode))
{ prinf(“Это %s не файл \n”,argv[1]);
return(1); }
p=mmap(0,sb.st_size,PROT_READ,MAP_SHARED,fd,0); //отображение
в память, второй параметр указывает на размер файла
if (p==MAP_FAILED) //ошибка
{ perror(“mmap”);
return(1); }
if (close(fd)==-1) //закрытие fd
{ perror(“close”);
return(1); }
for (len=0; len<sb.st_size; len++)
putchr(p[len]); //байты из области p
if (munmap(p,sb.st_size)==-1) //закрытие области отображения,
указывается адрес начала и размер области
{ perror(“munmap”);
return(1); }
}
Программа принимает в качестве аргумента имя текстового файла и выводит содержимое этого файла на экран дисплея.
Достоинствами системного вызова mmap можно назвать то, что при его использовании не требуется дополнительное копирования, выполняемое при вызове read и write, что процесс выполнения действий достигается путем прямой работы с ОП, что для поиска необходимых данных в отображении необходимо просто манипулировать указателем, а действия по позиционированию производить не требуется. Но отображение системного вызова mmap занимает целое число страниц, в памяти оно не должно выходить за пределы адресного пространства процесса, а манипулирование отображения в памяти требует определенной затраты ядра ОС.
Вызов синхронизации файла с отображением
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
Где flags может иметь следующие значения:
MS_ASYNC – т.е. асинхронно (изначально вызывается завершение, а потом ОС завершает запись)
MS_INVALIDATE – указывает, что все кэшированные ранее копии отображения аннулируются и синхронизируется текущее состояние данных
MS_SYNC – синхронизация выполняется синхронно
Сброс на диск всех изменений, внесенных в файл, отображается в память. В физический файл записывается или все содержимое области отображения, или участок области отображения. Синхронизация же производится, начиная с адреса addr, размером len, flags - флаги управления проведения синхронизации.
В случае успеха системный вызов msync возвращает значение 0. В случае ошибки – -1. Переменная errno может принимать следующие значения:
EINVAL – параметр flags включает оба значения, MS_SYNC и MS_ASYNC; установлен бит, отличный от трех допустимых флагов, или значение addr не выровнено по размеру страницы
ENOMEM – указанная область памяти (или ее часть) не входит в отображение
Каждый файл представляется структурой inode, которой присваивается адрес при помощи уникального для файловой системы числового значения, называемого номером inode (inode number). Inode — это и физический объект, находящийся на диске в Unix-подобной файловой системе, и концептуальная сущность, представляемая структурой данных в ядре Linux. В inode хранятся метаданные (metadata), связанные с файлом, такие, как разрешения на доступ к файлу, временная отметка, указывающая время последнего доступа к файлу, владелец, группа и размер файла, а также местоположение данных файла.
Далее приведены системные вызовы для получения значений метаданных файла:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstst(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
Где параметр path – это полный путь к файлу, а fd – открытый файловый дескриптор.
Следует заметить, что системный вызов lstat отличается от stat тем, что он позволяет получить информацию о файле типа символьная ссылка.
При успешном завершении вызовов возвращается 0 и в структуру stat (рассмотренную ниже) помещаются метаданные, а при ошибке возвращается значение -1 и устанавливаются коды ошибок.
struct stat
{
dev_t st_dev; //устройство, на котором хранится файл
ino_t st_ino; //номер inode (индексного дескриптора)
mode_t st_mode; //режимы использования файла
nlink_t st_nlink; //число жестких ссылок
uid_t st_uid; //идентификатор пользователя владельца
gid_t st_gid; //идентификатор группы владельца
dev_t st_rdev; //если этот файл – устройство, то описано устройство, которое файл представляет
off_t st_size; //размер файла в байтах
blksize_t st_blksize; //размер блока для эффективной реализации ввода/вывода
blkcnt_ st_blocks; //число блоков физической файловой системы, которое
занимает файл
time_t st_atime; //время последнего доступа к файлу
time_t st_mtime; //время последней модификации метаданных файла
time_t st_ctime; //время последней изменения файла
}
В случае успеха все три вызова возвращают значение 0 и записывают метаданные файла в предоставленную им структуру stat. В случае ошибки они возвращают -1 и присваивают переменной errno одно из следующих значений:
EACCESS – у вызывающего процесса отсутствуют разрешения на поиск для одного из каталогов в пути path (только для функций stat () и lstat ()).
EBADF – недопустимое значение fd (только для функции fstat()).
EFAULT – параметр path или buf содержит недопустимый указатель.
ELOOP – параметр path включает слишком много символических ссылок (только для функций stat() и Istat()).
ENAMETOOLONG – слишком длинное значение параметра path (только для функций stat() и Istat()).
ENOENT – компонент пути path не существует (только для функций stat() и Istat()).
ENOMEM – недостаточно памяти для выполнения данного запроса.
ENOTDIR – компонент пути path не является каталогом (только для функций stat() и Istat()).
Использование вызовов для получения значений метаданных файла:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static short o_ar[9]={0400,0200,0100,0040,0020,0010,0004,0002,0001}
static char perms[10]= “rwxrwxrwx”
int filedata(const char *pathname)
{
struct stat statbuf;
char d[10];
int j;
if (stat(pathname,&statbuf)==-1)
{ printf(“Ошибка stat %s\n”,pathname);
return(-1); }
for (j=0; j<9; j++)
{ if (statbuf.st_mode & o_ar[j]) d[j]=perms[j];
else d[j]=’-1’; }
d[9]=’\0’;
printf(“файл %s : \n”, pathname);
printf(“размер %ld байт \n”,statbuf.st_size);
printf(“User_id %d, Group_id %d\n”,statbuf.st_uid,statbuf.st_gid);
printf(“Права доступа: %s\n”,d);
return(0);
}
st_mode может принимать значения:
S_ISDIR - каталог
S_ISCHR - символьное устройство
S_ISBLK – блочное устройство
S_ISREG – простой файл
S_ISFIFO - канал
S_ISLINK – ссылка
S_ISSOCK – сокет
Есть системные вызовы, которые позволяют изменить права доступа к файлу:
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fcmod(int fd, mode_t mode);
Пример такого вызова: chmod(“./text1.t”,S_TRUSR|S_IWUSR)
Оба вызова chmod() и fchmod() устанавливают для файла разрешения, указанные при помощи параметра mode. В вызове chmod() параметр path содержит относительный или абсолютный путь к модифицируемому файлу. Для вызова fсhmod() файл указывается при помощи дескриптора fd.
Допустимые значения параметра mode, представляемого непрозрачным целочисленным типом mode_t, те же, что и значения, возвращаемые в поле st_mode структуры stat. Хотя это простые целочисленные значения, они могут иметь разный смысл в разных реализациях Unix. Для формирования допустимых значений параметра mode эти константы можно объединять между собой операцией двоичного ИЛИ. Например. (S_IRUSR | S_IRGRP) устанавливает для файла разрешения, предоставляющие возможность чтения файла его владельцу и группе.
Чтобы иметь возможность изменить разрешения файла, действительный идентификатор процесса, вызывающего chmod() или fchmod(), должен совпадать с идентификатором владельца файла или же процесс должен обладать характеристикой CAP_F0WNER.
В случае успеха оба вызова возвращают значение 0. В случае ошибки оба возвращают -1 и присваивают переменной errno один из следующих кодов ошибки:
EACCESS – у вызывающего процесса нет разрешений на поиск для компонента пути path (только для chmod()).
EBADF – недопустимый дескриптор файла fd (только для fchmod()).
EFAULT – параметр path содержит недопустимый указатель (только для chmod()).
EIO – в файловой системе произошла внутренняя ошибка ввода-вывода. Это очень плохая ситуация, которая может указывать на повреждение диска или файловой системы.
ELOOP – во время разрешения пути path ядро встретило слишком много символических ссылок (только для chmod()).
ENAMETOOLONG – слишком длинное значение параметра path (только для chmod()).
ENOENT – путь path не существует (только для параметра chmod().
ENOMEM – недостаточно памяти для выполнения данного запроса.
ENOTDIR – компонент пути path не является каталогом (только для chmod()).
EPERM – действительный идентификатор вызывающего процесса не соответствует владельцу файла, и у процесса отсутствует характеристика CAP_FOWNER.
EROFS – файл находится в файловой системе, доступной только для чтения.
В структуре stat в полях st_uid и st_gid хранятся владелец и группа файла соответственно. Три системных вызова позволяют пользователю менять эти два значения:
#include <sys/types.h>
#include <unistd.h>
int chown (const char *path, uid_t owner, gid_t group);
int lchown (const char *path, uid_t owner, gid_t group);
int fchown (int fd, uid_t owner, gid_t group): ,
Вызовы chown() и Ichown() определяют владение файлом, указанным при помощи пути в параметре path. Они работают одинаково для всех файлов, за исключением символических ссылок.
В случае успеха все три вызова делают владельцем файла пользователя owner, группой файла группу group и возвращают значение 0. Если параметр owner или group равен -1, то соответствующее значение не устанавливается. Только процесс, владеющий характеристикой CAP_CH0WN (обычно это процесс, принадлежащий пользователю root), может менять владельца файла. Владелец файла может сменить группу файла на любую другую, членом которой он является; процессы с характеристикой CAP_CH0WN имеют право менять группу файла на любое другое значение.
В случае ошибки вызовы возвращают -1 и присваивают переменной errno одно из следующих значений:
EACCESS – у вызывающего процесса отсутствуют разрешения на поиск для компонента пути path (только для chown() и Ichown()).
EBADF – недопустимое значение fd (только для fchown()).
EFAULT – недопустимое значение path (только для chown() и Ichown()).
EIO – внутренняя ошибка ввода-вывода.
ELOOP – во время разрешения пути path ядро встретило слишком много символических ссылок (только для chown() и Ichown()).
ENAMETOOLONG – слишком длинное значение path (только для chown() и Ichown()).
ENOENT – файл не существует.
ENOMEM – недостаточно памяти для выполнения данного запроса.
ENOTDIR – компонент пути path не является каталогом (только для chown() и lchown()).
EPERM – у вызывающего процесса отсутствуют необходимые права для изменения владельца или группы в соответствии с запросом.
EROFS – файловая система доступна только для чтения.