- •Введение
- •1 Тема 7. Подсистема управления вводом-выводом
- •1.1 Язык С как стандарт взаимодействия с ОС
- •1.2 Системные операции для работы с файловой системой
- •1.2.1 Системные вызовы open() и close()
- •1.2.2 Системные вызовы read() и write()
- •1.2.3 Системный вызов lseek()
- •1.3 Создание специальных файлов
- •1.4 Запрос информации о статусе файлов
- •1.5 Каналы
- •1.5.1 Полудуплексные каналы UNIX
- •1.5.2 Именованные каналы FIFO
- •1.6 Дублирование дескрипторов файлов
- •1.7 Монтирование и демонтирование ФС
- •1.8 Ссылки на имена файлов
- •1.9 Лабораторная работа по теме №7
- •1.9.1 Интегрированная среда разработки Eclipse
- •1.9.2 Список заданий выполняемых работ
- •1.9.3 Проблема типов в языке С
- •1.9.4 Анализ структуры MBR блочных устройств
- •1.9.5 Запрос информации о статусе файлов
- •1.9.6 Неименованные каналы ядра ОС
- •1.9.7 Именованные каналы FIFO
- •1.9.8 Монтирование flashUSB
- •1.9.9 Работа с именами файлов
- •2 Тема 8. Подсистема управления памятью
- •2.1 Классификация способов управления ОЗУ
- •2.2 Программный и аппаратный способы адресации памяти
- •2.3 Страничная и сегментная адресации памяти
- •2.4 Комбинированный способ адресации памяти
- •2.5 Лабораторная работа по теме №8
- •2.5.1 Структура поцесса
- •2.5.2 Определяемые сегменты процесса
- •2.5.3 Создание и удаление процессов из памяти
- •2.5.4 Динамическое выделение и освобождение памяти процесса
- •3 Тема 9. Базовое взаимодействие процессов
- •3.1 Подсистема управления процессами
- •3.2 Синхронизация процессов
- •3.3 Стандарты POSIX
- •3.4 Системные вызовы ОС по управлению процессами
- •3.5 Системный вызов fork() и каналы процесса
- •1.5.1 Пример использования каналов процессов
- •1.5.2 Имитация конвейеров языка shell
- •3.6 Нити (Threads)
- •3.7 Сигналы POSIX
- •3.8 Лабораторная работа по теме №9
- •3.8.1 Системные вызовы общей группы
- •3.8.2 Управление потоками процессов
- •3.8.3 Обработка сигналов ОС
- •4 Тема 10. Асинхронное взаиодействие процессов
- •4.1 Проблемы распределения ресурсов ОС
- •4.2 Системный пакет IPC
- •4.3 Утилиты управления средствами пакета IPC
- •Утилита ipcmk
- •Утилита ipcs
- •Утлита ipcrm
- •4.4 Семафоры
- •4.5 Задача об обедающих философах
- •4.5.1 Описание задачи
- •4.5.2 Выбор стратегии решения
- •4.5.3 Модель философа
- •4.5.4 Программа-монитор
- •4.6 Лабораторная работа по теме №10
- •4.6.1 Синхронизация двух процессов
- •4.6.2 Задача «Обедающие философы»
- •5 Тема 11. Эффективное взаиодействие процессов
- •5.1 Прикладные средства пакета IPC
- •5.2 Разделяемые сегменты памяти
- •5.3 Задача о читателях и писателях
- •5.4 Передача сообщений
- •5.5 Лабораторная работа по теме №11
- •5.5.1 Задачи с разделяемыми сегментами памяти
- •5.5.2 Программы передачи сообщений
- •6 Тема 12. Системная шина D-Bus
- •6.1 Графические среды ОС
- •6.2 Рабочий стол пользователя
- •6.3 Различия графических сред ОС
- •6.4 X-сервер UNIX
- •6.5 Архитектура шины D-Bus
- •6.5.2 Бибиотека libdbus
- •6.5.3 Проекции ПО D-Bus на языки программирования
- •6.6 Лабораторная работа по теме №12
- •6.6.1 Утилита qdbus
- •6.6.2 Взаимодействие через шину с приложением evince
- •Заключение
- •Список использованных источников
27
Рисунок 1.9 - Структура Partition Table для устройства с GPT
Замечание
Студент постоянно должен помнить о проблемах использования структур языка С, в плане сопоставления их с данными, имеющими прямую адресную привязку.
1.2.3 Системный вызов lseek()
Системный вызов lseek(...) устанавливает позицирование в файле, определенном его дескриптором. В случае успешного завершения операции, возвращается место новой позиции в байтах от начала файла.
Общий формат вызова имеет вид:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
Для вызова этой функции используется три аргумента:
fd — целочисленный номер дескриптора открытого файла; offset — целочисленное значение смещения в байтах; whence — директива, задающая интерпретацию смещения:
• SEEK_SET = 0 - смещение устанавливается в offset байт;
•SEEK_CUR = 1 - смещение устанавливается как текущее смещение плюс offset байт;
•SEEK_END = 2 - смещение устанавливается как размер файла плюс offset
28
байт.
В случае ошибки, функция lseek(...) возвращает значение -1 и необходимо анализировать переменную errno:
•EBADF - fd не является дескриптором открытого файла;
•ESPIPE - fd ассоциирован с каналом, сокетом или FIFO;
•EINVAL - whence не является одним из значений SEEK_SET, SEEK_CUR,
SEEK_END или смещение в файле, которое получилось в результате является отрицательным;
•EOVERFLOW - получившееся в результате смещение не может быть представлено типом off_t.
Замечание
Функция lseek() позволяет задавать смещения, которые будут находиться за существующим концом файла, но это не изменяет размер файла. Если позднее по этому смещению будут записаны данные, то последующее чтение в промежутке от конца файла до этого смещения, будет возвращать нулевые байты, пока в этот промежуток не будут фактически записаны данные.
Имеются и другие нововведения, которые мы не будем здесь рассматривать. В любом случае, перед примененим этой функции, следует обратиться к официальному руководству команой: man 2 lseek
Продемострируем возможности функции lseek() на примере задачи анализа содержимого Partition Table, допустим с целью экономии используемой памяти ЭВМ. Для этого:
•за основу возьмем исходный текст листинга 1.4;
•удалим структуру pt_struct, оставив только нужные поля для записи текущих значений;
•размер буфера чтения уменьшим до 4-х байт.
Общий алгоритм работы программы построим следующим образом:
•откроем файл, сделаем смещение на местоположение сигнатуры и проверим ее наличие;
•сделаем смещение на начало местоположения Partition Table;
•в цикле прочитаем и выведем структуру этой таблицы.
На листинге 1.5 представлен вариант реализации такого алгоритма.
Листинг 1.5 — Второй вариант чтения структуры Partition Table
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/hdreg.h> #include <fcntl.h> #include <sys/types.h> #include <errno.h>
29
int main(int argc, char *argv[], char *envp[]) {
printf("Число параметров программы lab1.4: argc=%i\n", argc); if(argc < 2) { // Нет аргумента - завершаем работу
puts("Запусти: sudo ./lab1.4 /dev/sda\n"); return 1;
}
printf("Исследуем устройство: %s\n", argv[1]);
//Введем обозначение типов данных: typedef unsigned char u8;
typedef unsigned short u16; typedef unsigned int u32;
//Нужные поля записи Partition Table
//u8 bootable; // +0 - флаг активности раздела (0 или 80H)
u8 type_part; |
// |
+4 |
- системный идентификатор - System ID |
|
u32 |
sect_before; |
// |
+8 |
- число секторов перед разделом |
u32 |
sect_total; |
//+12 - размер раздела в секторах (число секторов в разделе) |
// Переменные программы |
|
int sd; |
// Дескриптор файла |
ssize_t z; |
// Количество считанных байт |
off_t of; |
// Количество считанных байт |
char buf[4]; |
// Буфер чтения данных |
u8 status = 0; |
// Рабочая переменная |
u16 * ss; |
// Рабочая переменная |
int i; |
// Индекс цикла |
// Открываем устройство - только для чтения sd=open(argv[1],O_RDONLY);
if(sd < 0) { |
// Если не открыто - завершаем работу |
perror(argv[1]); |
|
return 2; |
|
}else{
printf("Открыт файл %s: с дескриптором = %d\n", argv[1], sd);
}
// Читаем сигнатуру из сектора LBA0 of = lseek(sd, 510, SEEK_SET);
if (of != 510) {
printf("Ошибка: смещение не 510 байт, а %lu\n", of); close(sd);
return 1;
}
z = read(sd, buf, 2);
printf("Прочитали LBA0=%d байт\n", (int)z); if (z != 2) {
puts("Ошибка: прочитали не 2 байта"); close(sd);
return 1;
}
//Проверяем сигнатуру ss = (u16 *)buf;
if (*ss == 0xAA55){
printf("Прочитали сектор MBR: сигнатура = 0x%x\n", *ss); }else{
printf("Ошибка: Это не сектор MBR: сигнатура = 0x%x\n", *ss); return 1;
}
puts("--------------------------"); of = lseek(sd, 446, SEEK_SET);
if (of != 446) {
printf("Ошибка: смещение не 446 байт, а %lu\n", of);
30
close(sd); return 1;
}
//Цикл просмотра Partition Table for (i = 0; i < 4; i++) {
z = read(sd, buf, 4); status = (u8)buf[0];
printf("%i Статус=%x\t", i + 1, status); if ((status != 0) && (status != 0x80)) {
puts(" ошибочный статус"); |
|
|
|
of = lseek(sd, 12, SEEK_CUR); |
|
|
|
continue; |
|
|
|
} |
|
|
|
z = read(sd, buf, 4); |
|
|
|
type_part = (u8)buf[0]; |
|
|
|
if (status == 0) |
printf("Тип=%x\t |
", type_part); |
|
if (status == 0x80) printf("Тип=%x\t* |
", type_part); |
||
z = read(sd, (char *)§_before, |
4); |
|
|
z = read(sd, (char *)§_total, |
4); |
|
printf("Начало=%u\tДлина=%u\n", sect_before, sect_total);
}
close(sd); puts("--------------------------"); puts("Завершили работу...");
return EXIT_SUCCESS;
}
На рисунке 1.10 представлен вывод программы, приведенной на листинге 1.5. Хорошо видно, что содержимое этого рисунка, с точностью до деталей, совпадает с выводом программы, представленной на рисунке 1.8.
Рисунок 1.10 — Второй вариант чтения структуры Partition Table