Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методические указания Редакция 1.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
100.42 Кб
Скачать

Варианты заданий.

  1. Написать программу, которая осуществляет вывод окружения на экран. Заменить значение любых двух переменных окружения.

  2. Написать программу, которая осуществляет вывод 5 первых переменных окружения на экран. Заменить значение двух переменных, удалить одну переменную.

  3. Написать программу, которая выводит указанную переменную на экран. Копировать ее значение в другую переменную, а указанную удалить.

  4. Написать программу, которая выводит 10 переменных окружения. Изменить значение 5 из них, добавить новую переменную, удалить первые 3 переменные.

  5. Написать программу, которая выводит 4 указанных переменных окружения. Изменить значение двух переменных и удалить одну

  6. Написать программу, которая выводит 10 последних переменных окружения. Поменять местами значение первых двух с последними двумя переменными.

Контрольные вопросы

  1. Что такое окружение?

  2. Какие переменные окружения вы знаете?

  3. Назовите и опишите функции для работы с окружением.

Лабораторная работа № 3 ввод/вывод в linux. Обработка ошибок

Цель работы: ознакомиться с работой базовых системных вызовов и библиотечных функций для работы с файлами; научиться обрабатывать ошибки после их вызова.

Программа работы

  1. Написать программу, осуществляющую создание, чтение, запись и закрытие файла с использованием системных функций. Предусмотреть обработку ошибок и наличие параметров.

  2. Откомпилировать программу, и запустить и получить результаты.

  3. Продемонстрировать преподавателю корректную работу программы и «спровоцировать» нештатные ситуации, в результате которых выдаются сообщения об ошибке.

  4. Ответить на контрольные вопросы.

  5. Составить отчет Краткая теория.

Совокупность операций, при помощи которых программа читает и записывает файлы, называется вводом-выводом (input/output). Если программа читает и записывает файлы посредством прямого обращения к ядру операционной системы, то такой ввод-вывод называется низкоуровневым.

Чтобы понять работу механизмов низкоуровневого ввода-вывода в Linux, рассмотрим сначала принципы чтения и записи файлов в стандартной библиотеке языка С.

Любую модель файлового ввода-вывода можно условно поделить на следующие составляющие:

  1. абстракция файла;

  2. механизмы открытия и закрытия файлов;

  3. механизмы чтения и записи файлов;

  4. механизмы произвольного доступа к данным в файле.

Применяя эти понятия к вводу-выводу стандартной библиотеки языка С, мы имеем возможность изучать низкоуровневый ввод-вывод в сравнении с аналогичными механизмами высокого уровня.

Абстракция файла в стандартной библиотеке языка С реализуется через тип данных file. Для этого создается указатель, который в дальнейшем будет участвовать в файловых операциях:

FILE * myfile;

Особого рассмотрения требуют следующие глобальные экземпляры:

extern FILE * stdin; extern FILE * stdout; extern FILE * stderr;

Эта "троица" представляет собой стандартный ввод (stdin), стандартный вывод (stdout) и стандартный поток ошибок (stderr). Ввод-вывод в Linux построен на одной аксиоме, которая гласит, что любая операция чтения или записи данных сводится к файловому вводу-выводу. Таким образом, любое "нечто", способное принимать или отправлять данные (клавиатура, экран, аппаратные устройства, сетевые соединения и т. п.) можно представить в виде файла.

Стандартный ввод, стандартный вывод и стандартный поток ошибок — это виртуальные "порталы", через которые система взаимодействует с пользователем. Как правило, стандартный ввод связан с драйвером клавиатуры, а стандартный вывод и стандартный поток ошибок ассоциируются с дисплеем компьютера.

Механизмы открытия и закрытия файлов в стандартной библиотеке языка С представлены следующими функциями:

FILE * fopen (const char * FLOCATION, const char * OPENJMODE); FILE * freopen (const char * FLOCATION, const char * OPEN_MODE,FILE * FP); FILE * fClose (FILE * FP) ;

Механизмы чтения и записи файлов реализованы в стандартной библиотеке в виде следующих функций:

int fgetc (FILE * FP) ; int fputc (int byte, FILE * FP); char * fgets (char * STR, int SIZE, FILE * FP); int fputs (const char * STR, FILE * FP) ; int fscanf (FILE * FP, const char * FMT, ...); int fprintf (FILE * FP, const char * FMT, . . .) ; int vfscanf (FILE * FP, const char * FMT, va_list ap); int vfprintf (FILE * FP, const char * FMT, va_list ap);

Файловый ввод-вывод предполагает, что чтение и запись данных осуществляются последовательно. Для этого к абстракции файла привязывается понятие текущей позиции ввода-вывода. Текущая позиция устанавливается в начало файла в момент его открытия. Операции чтения/записи смещают эту позицию вперед на количество прочитанных или записанных байтов. Для принудительного изменения текущей позиции используются механизмы произвольного доступа (random access). В стандартной библиотеке языка С произвольный доступ к данным осуществляется при помощи следующих механизмов:

  • #define SEEK_SET 0

  • «define SEEK_CUR 1

  • «define SEEK_END 2

  • typedef struct {...} fpos_t;

  • int fseek (FILE * FP, long int OFFSET, int WHENCE);.

  • int fgetpos (FILE * FP, fpos_t * POSITION);

  • int fsetpos (FILE * FP, fpos_t * POSITION!;

  • long int ftell (FILE * FP) ;

  • void rewind (FILE * FP) ;

Рассмотрим пример программы, которая читает из файла "хвост" заданного размера и выводит его на экран. Первый аргумент программы— имя файла, второй аргумент— размер "хвоста". Все операции ввода-вывода осуществляются средствами стандартной библиотеки языка С.

Программа mytail.с

#include <stdlib.h> #include <stdio.h> int main (int argc, char ** argv) { FILE * infile; int ch; } long int nback; if (argc < 3) { fprintf (stderr, "Too few arguments\n"); return 1; } infile = fopen ( argv[1], "r"); if (infile == NULL) { fprintf (stderr, "Cannot open " "input file (%s)\n", argv[1]); return 1; } nback = abs (atoi (argv[2])); fseek (infile, 0, SEEK_END); if (nback > ftell (infile)) fseek (infile, 0, SEEK_SET); else fseek (infile, -nback, SEEK_END); while ((ch = fgetc (infile)) != EOF) fputc (ch, stdout); fclose (infile); return 0;

Рассмотрим вкратце, что делает эта программа. Сначала создается абстракция файла— указатель infile. Функция fopen открывает файл в режиме "только для чтения" (read only). Обратите внимание, что введенный размер "хвоста" может превышать размер файла. Поэтому в программу включен алгоритм проверки, который устанавливает текущую позицию ввода-вывода в начало файла, если "хвост" слишком велик.

После установки текущей позиции ввода-вывода в начало "хвоста", осуществляется его посимвольное чтение с одновременной записью в выходной файл, в качестве которого выступает стандартный поток вывода (stdout). Цикл продолжается до тех пор, пока не будет достигнут конец входного файла.

Функция fclose () закрывает файл. В этом процессе есть свои тонкости. Дело в том, что в Linux применяются механизмы буферизации файлового ввода-вывода. Это означает, что функция fputc (), например, реально записывает данные не в устройство ввода-вывода, а в буфер, находящийся в оперативной памяти. Реальная запись данных в устройство производится по усмотрению системы. Функция fclose отправляет системе запрос на синхронизацию. Но следует понимать, что такой запрос лишь принимается на рассмотрение, но не гарантирует реальную запись данных.

Иногда нужно направить системе запрос на синхронизацию, не закрывая файл. Для этого в стандартной библиотеке языка С предусмотрена следующая функция:

int fflush (FILE * FP);

Существует еще один нюанс: в стандартной библиотеке языка С используется собственный механизм буферизации. Таким образом, fflush отвечает лишь за отправку на запись собственных буферов, а заставить ядро синхронизировать данные средствами стандартной библиотеки языка С практически невозможно.

Даже если файл открыт в режиме "только для чтения", его все равно следует закрывать после всех операций ввода-вывода. Во-первых, это хороший стиль программирования. Во-вторых, открытые файлы расходуют системные ресурсы, которые нужно своевременно освобождать.

Рассмотрим теперь особенности низкоуровневого ввода-вывода в Linux, сравнивая его с библиотечным вводом-выводом языка С.

В ядре Linux в качестве абстракций файлов используются файловые дескрипторы. В отличие от указателей типа file, дескрипторы представляют собой просто целые числа (int). Ядро создает для каждого процесса индивидуальную таблицу файловых дескрипторов, в которой хранится информация об открытых файлах. Итак, абстракция файла в рамках концепции низкоуровневого ввода-вывода представляет собой целое число, являющееся номером записи в таблице дескрипторов текущего процесса.

После открытия файла в таблице дескрипторов создается новая запись с уникальным номером. Чтобы избежать "узурпации" всех системных ресурсов одним процессом, ядро ограничивает размер таблицы файловых дескрипторов определенным значением.

Механизмы открытия и закрытия файлов представлены в ядре следующими системными вызовами:

int open (const char * FNAME, int OFLAGS, mode_t

MODE); int open (const char * FNAME, int OFLAGS); int creat (const char

* FNAME, mode_t MODE),-int close (int FD);

Чтение и запись файлов осуществляются следующими механизмами:

ssize_t read (int FD, void * BUFFER, size_t SIZE); ssize_t write (int FD, const void * BUFFER, size_t SIZE); ssize_t readv (int FD, const struct iovec * VECTOR," int SIZE) ; ssize_t writev (int FD, const struct iovec * VECTOR, int SIZE);

Наконец, при помощи следующего системного вызова осуществляется произвольный доступ к данным в файле:

off_t Iseek (int FD, off_t OFFSET, int WHENCE);

Представленные функции являются системными вызовами. Они отличаются от обычных функций тем, что реализованы в ядре. Иными словами, ядро является для них подобием библиотеки.