Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 20 21 22 23 24 25 26  27 28 29 30 31 32 33 ... 87
"символьную ссылку" вызовом

    symlink(link_to_filename,link_file_name_to_be_created);

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

    char linkbuf[ MAXPATHLEN + 1]; /* куда поместить ответ */
    int len = readlink(pathname, linkbuf, sizeof linkbuf);
    linkbuf[len] = '\0';

Системный вызов stat автоматически разыменовывает символьные ссылки и выдает информа-
цию  про  указуемый файл. Системный вызов lstat (аналог stat за исключением названия)
выдает информацию про саму ссылку (тип файла S_IFLNK).   Коды  доступа  к  ссылке  не
имеют  никакого  значения для системы, существенны только коды доступа самого указуе-
мого файла.
     Еще раз: символьные ссылки удобны для указания  файлов  и  каталогов  на  другом
диске.   Пусть  у  вас  не помещается на диск каталог /opt/wawa. Вы можете разместить
каталог wawa на диске USR: /usr/wawa. После чего создать символьную ссылку из /opt:

            ln -s /usr/wawa /opt/wawa

чтобы программы видели этот каталог под его прежним именем /opt/wawa.
     Еще раз:
hard link
     - то, что создается системным вызовом link, имеет тот же I-node (индексный узел,
     паспорт), что и исходный файл.  Это просто альтернативное имя файла, учитываемое
     в поле di_nlink в I-node.
____________________
   [*] BSD - семейство UNIX-ов из University of California, Berkley.  Berkley  Software
Distribution.

А. Богатырев, 1992-95                  - 145 -                              Си в UNIX

symbolic link
     - создается вызовом symlink. Это отдельный самостоятельный файл,  с  собственным
     I-node.  Правда,  коды  доступа  к  этому  файлу не играют никакой роли; значимы
     только коды доступа указуемого файла.

4.7.  Напишите программу, которая находит в файле символ @  и  выдает  файл  с  этого
места  дважды.  Указание: для запоминания позиции в файле используйте вызов lseek() -
позиционирование указателя чтения/записи:

    long offset, lseek();
       ...
    /* Узнать текущую позицию чтения/записи:
     * сдвиг на 0 от текущей позиции. lseek вернет новую
     * позицию указателя (в байтах от начала файла). */
    offset = lseek(fd, 0L, 1);  /* ftell(fp) */

А для возврата в эту точку:

       lseek(fd, offset, 0);    /* fseek(fp, offset, 0) */

По поводу lseek надо помнить такие вещи:
-    lseek(fd, offset, whence) устанавливает указатель  чтения/записи  на  расстояние
     offset байт
    при whence:
      0    от начала файла     RWptr  = offset;
      1    от текущей позиции  RWptr += offset;
      2    от конца файла      RWptr  = длина_файла + offset;

    Эти значения whence можно обозначать именами:
    #include 
      0    это   SEEK_SET
      1    это   SEEK_CUR
      2    это   SEEK_END

-    Установка указателя чтения/записи - это  виртуальная  операция,  т.е.  реального
     подвода  магнитных  головок и вообще обращения к диску она не вызывает. Реальное
     движение  головок  к  нужному  месту  диска  произойдет  только  при   операциях
     чтения/записи read()/write().  Поэтому lseek() - дешевая операция.
-    lseek() возвращает новую  позицию  указателя  чтения/записи  RWptr  относительно
     начала файла (long смещение в байтах). Помните, что если вы используете это зна-
     чение, то вы должны предварительно описать lseek как функцию, возвращающую длин-
     ное целое: long lseek();
-    Аргумент offset должен иметь тип long (не ошибитесь!).
-    Если поставить указатель за конец файла (это  допустимо!),  то  операция  записи
     write()  сначала заполнит байтом '\0' все пространство от конца файла до позиции
     указателя; операция read() при попытке чтения из-за конца файла  вернет  "прочи-
     тано 0 байт". Попытка поставить указатель перед началом файла вызовет ошибку.
-    Вызов lseek() неприменим к pipe и FIFO-файлам, поэтому попытка сдвинуться  на  0
     байт выдаст ошибку:

         /* это стандартная функция */
         int isapipe(int fd){
              extern errno;
              return (lseek(fd, 0L, SEEK_CUR) < 0 && errno == ESPIPE);
         }

     выдает "истину", если fd - дескриптор "трубы"(pipe).

А. Богатырев, 1992-95                  - 146 -                              Си в UNIX

4.8.  Каков будет эффект следующей программы?

    int fd = creat("aFile", 0644); /* creat создает файл
        открытый на запись, с доступом rw-r--r-- */
    write(fd, "begin", 5 );
    lseek(fd, 1024L * 1000, 0);
    write(fd, "end", 3 );
    close(fd);

Напомним, что при записи в файл, его  длина  автоматически  увеличивается,  когда  мы
записываем информацию за прежним концом файла.  Это вызывает отведение места на диске
для хранения новых данных (порциями, называемыми блоками - размером от 1/2 до 8 Кб  в
разных  версиях).   Таким  образом,  размер файла ограничен только наличием свободных
блоков на диске.
     В нашем примере получится файл длиной 1024003 байта. Будет  ли  он  занимать  на
диске 1001 блок (по 1 Кб)?
     В системе UNIX - нет! Вот кое-что про механику выделения блоков:
-    Блоки располагаются на диске не обязательно подряд - у каждого файла есть специ-
     альным образом организованная таблица адресов его блоков.
-    Последний блок файла может быть занят не целиком (если  длина  файла  не  кратна
     размеру  блока), тем не менее число блоков у файла всегда целое (кроме семейства
     BSD, где блок может делиться на фрагменты, принадлежащие разным файлам).  Опера-
     ционная  система в каждый момент времени знает длину файла с точностью до одного
     байта и не позволяет нам "заглядывать" в остаток блока, пока при  своем  "росте"
     файл не займет эти байты.
-    Блок на диске физически выделяется лишь после операции записи в этот блок.

     В нашем примере: при создании файла его размер 0, и ему выделено 0  блоков.  При
первой  записи  файлу будет выделен один блок (логический блок номер 0 для файла) и в
его начало запишется "begin".  Длина файла станет равна 5 (остаток блока - 1019  байт
- не используется и файлу логически не принадлежит!).  Затем lseek поставит указатель
записи далеко за конец файла и write запишет в 1000-ый блок слово "end". 1000-ый блок
будет  выделен  на диске. В этот момент у файла "возникнут" и все промежуточные блоки
1..999. Однако они будут только "числиться за файлом", но на диске отведены не  будут
(в  таблице  блоков  файла  это  обозначается  адресом  0)!   При чтении из них будут
читаться байты '\0'. Это так называемая "дырка" в файле. Файл  имеет  размер  1024003
байта,  но  на  диске  занимает всего 2 блока (на самом деле чуть больше, т.к.  часть
таблицы блоков файла тоже находится в специальных блоках  файла).   Блок  из  "дырки"
станет реальным, если в него что-нибудь записать.
     Будьте готовы к тому, что "размер файла" (который, кстати, можно узнать  систем-
ным  вызовом  stat)  -  это  в  UNIX не то же самое, что "место, занимаемое файлом на
диске".

4.9.  Найдите ошибки:

    FILE *fp;
        ...
    fp = open( "файл", "r" ); /* открыть */
    close(fp);                /* закрыть */

Ответ: используется системный вызов open() вместо  функции  fopen();  а  также  close
вместо  fclose,  а их форматы (и результат) различаются!  Следует четко различать две
существующие в Си модели обмена с  файлами:  через  системные  вызовы:  open,  creat,
close,  read,  write,  lseek; и через библиотеку буферизованного обмена stdio: fopen,
fclose, fread, fwrite, fseek, getchar, putchar, printf, и.т.д.  В первой из них обра-
щение к файлу происходит по целому fd - дескриптору файла, а во втором - по указателю
FILE *fp - указателю на файл.  Это параллельные механизмы  (по  своим  возможностям),
хотя  второй является просто надстройкой над первым. Тем не менее, лучше их не смеши-
вать.

А. Богатырев, 1992-95                  - 147 -                              Си в UNIX

4.10.  Доступ к диску (чтение/запись) гораздо (на несколько порядков) медленнее,  чем
доступ  к данным в оперативной памяти. Кроме того, если мы читаем или записываем файл
при помощи системных вызовов маленькими порциями (по 1-10 символов)

    char c;
    while( read(0, &c, 1)) ... ; /* 0 - стандартный ввод */

то мы проигрываем еще в одном: каждый системный вызов - это обращение к ядру операци-
онной  системы. При каждом таком обращении происходит довольно большая дополнительная
работа (смотри главу "Взаимодействие с UNIX"). При этом накладные  расходы  на  такое
посимвольное чтение файла могут значительно превысить полезную работу.
     Еще одной проблемой является то, что системные вызовы работают с  файлом  как  с
неструктурированным массивом байт; тогда как человеку часто удобнее представлять, что
файл поделен на строки, содержащие  читабельный  текст,  состоящий  лишь  из  обычных
печатных символов (текстовый файл).
     Для решения этих двух проблем была  построена  специальная  библиотека  функций,
названная  stdio  -  "стандартная  библиотека  ввода/вывода"  (standard  input/output
library). Она является частью библиотеки /lib/libc.a и представляет собой  надстройку
над  системными вызовами (т.к. в конце концов все ее функции время от времени обраща-
ются к системе, но гораздо реже, чем если  использовать  сисвызовы  непосредственно).
Небезызвестная директива #include  включает в нашу программу файл с объявле-
нием форматов данных и констант, используемых этой библиотекой.
     Библиотеку stdio можно назвать библиотекой буферизованного обмена, а также  биб-
лиотекой  работы с текстовыми файлами (т.е. имеющими разделение на строки), поскольку
для оптимизации обменов с диском (для уменьшения числа обращений к нему и  тем  самым
сокращения  числа  системных вызовов) эта библиотека вводит буферизацию, а также пре-
доставляет несколько функций для работы со строчно-организованными файлами.
     Связь с файлом в этой модели обмена осуществляется  уже  не  при  помощи  целого
числа  - дескриптора файла (file descriptor), а при помощи адреса "связной" структуры
FILE. Указатель  на  такую  структуру  условно  называют  указателем  на  файл  (file
pointer)[*].  Структура FILE содержит в себе:
-    дескриптор fd файла для обращения к системным вызовам;
-    указатель на буфер, размещенный в памяти программы;
-    указатель на текущее место в буфере, откуда надо выдать или куда  записать  оче-
     редной символ; этот указатель продвигается при каждом вызове getc или putc;
-    счетчик оставшихся в буфере символов (при  чтении)  или  свободного  места  (при
     записи);
-    режимы открытия файла (чтение/запись/чтение+запись) и текущее  состояние  файла.
     Одно из состояний - при чтении файла был достигнут его конец[**];
-    способ буферизации;

Предусмотрено несколько стандартных структур FILE, указатели  на  которые  называются
stdin,  stdout и stderr и связаны с дескрипторами 0, 1, 2 соответственно (стандартный
ввод, стандартный вывод, стандартный вывод ошибок). Напомним, что эти каналы  открыты
неявно  (автоматически)  и,  если  не перенаправлены, связаны с вводом с клавиатуры и
выводом на терминал.
     Буфер в оперативной памяти нашей программы создается (функцией malloc) при  отк-
рытии  файла  при помощи функции fopen().  После открытия файла все операции обмена с
файлом происходят не по 1 байту, а большими порциями размером с буфер - обычно по 512
байт (константа BUFSIZ).
     При чтении символа

            int c; FILE *fp = ... ;
            c = getc(fp);
____________________
   [*] Это не та "связующая" структура file в ядре, про которую шла речь  выше,  а  ЕЩЕ
одна - в памяти самой программы.
   [**]  Проверить  это  состояние позволяет макрос feof(fp); он истинен, если конец был
достигнут, ложен - если еще нет.
Предыдущая страница Следующая страница
1 ... 20 21 22 23 24 25 26  27 28 29 30 31 32 33 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама