Главная · Поиск книг · Поступления книг · 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 ... 19 20 21 22 23 24 25  26 27 28 29 30 31 32 ... 87
____________________
   [*] Заметим, что на самом деле коды доступа у нового файла будут равны
     di_mode = (коды_доступа & ~u_cmask) | IFREG;
(для каталога вместо IFREG будет IFDIR), где маска u_cmask задается системным вызовом
     umask(u_cmask);
(вызов выдает прежнее значение маски) и в дальнейшем наследуется всеми потомками дан-
ного процесса (она хранится в u-area процесса).  Эта маска позволяет запретить доступ
к определенным операциям для всех создаваемых нами файлов, несмотря на явно  заданные
коды доступа, например
     umask(0077); /* ???------ */
делает значащими только первые 3 бита кодов доступа (для владельца файла).  Остальные
биты будут равны нулю.
     Все это относится и к созданию каталогов вызовом mkdir.

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

     существовал - срабатывает O_CREAT и файл создается.  Это позволяет  предохранить
     уже существующие файлы от уничтожения.

Файл удаляется при помощи

    int unlink(char имя_файла[]);

У каждой программы по умолчанию открыты три первых дескриптора, обычно связанные

    0 - с клавиатурой (для чтения)
    1 - с дисплеем    (выдача результатов)
    2 - с дисплеем    (выдача сообщений об ошибках)

Если при вызове close(fd) дескриптор fd не соответствует открытому файлу (не был отк-
рыт) - ничего не происходит.
     Часто используется такая метафора:  если  представлять  себе  файлы  как  книжки
(только  чтение)  и блокноты (чтение и запись), стоящие на полке, то открытие файла -
это выбор блокнота по заглавию на его обложке и открытие  обложки  (на  первой  стра-
нице).   Теперь можно читать записи, дописывать, вычеркивать и править записи в сере-
дине, листать книжку!  Страницы можно сопоставить блокам файла (см. ниже), а  "полку"
с книжками - каталогу.

4.2.  Напишите программу, которая копирует содержимое одного файла в  другой  (новый)
файл.   При этом используйте системные вызовы чтения и записи read и write.  Эти сис-
вызовы пересылают массивы байт из памяти в файл и наоборот. Но любую переменную можно
рассматривать как массив байт, если забыть о структуре данных в переменной!
     Читайте и записывайте файлы большими кусками, кратными 512 байтам. Это  уменьшит
число обращений к диску.  Схема:

    char buffer[512]; int n; int fd_inp, fd_outp;
            ...
    while((n = read (fd_inp,  buffer, sizeof buffer)) > 0)
               write(fd_outp, buffer, n);

Приведем несколько примеров использования write:

    char c = 'a';
    int  i = 13, j = 15;
    char s[20] = "foobar";
    char p[]   = "FOOBAR";
    struct { int x, y; } a = { 666, 999 };
    /* создаем файл с доступом    rw-r--r-- */
    int fd = creat("aFile", 0644);
    write(fd, &c, 1);
    write(fd, &i, sizeof i);  write(fd, &j, sizeof(int));
    write(fd, s,  strlen(s)); write(fd, &a, sizeof a);
    write(fd, p,  sizeof(p) - 1);
    close(fd);

Обратите внимание на такие моменты:
-    При использовании write() и read() надо передавать  АДРЕС  данного,  которое  мы
     хотим записать в файл (места, куда мы хотим прочитать данные из файла).
-    Операции read и write возвращают число действительно прочитанных/записанных байт
     (при  записи  оно  может  быть  меньше указанного нами, если на диске не хватает
     места; при чтении - если от позиции чтения  до  конца  файла  содержится  меньше
     информации, чем мы затребовали).
-    Операции read/write продвигают указатель чтения/записи

         RWptr += прочитанное_или_записанное_число_байт;

     При открытии файла указатель стоит на начале файла: RWptr=0.   При  записи  файл

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

     если надо автоматически увеличивает свой размер.  При чтении - если мы достигнем
     конца файла, то read будет возвращать "прочитано 0 байт" (т.е. при чтении указа-
     тель чтения не может стать больше размера файла).
-    Аргумент сколькоБайт имеет тип unsigned, а не просто int:

    int n = read (int fd, char *адрес, unsigned сколькоБайт);
    int n = write(int fd, char *адрес, unsigned сколькоБайт);

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

4.2.1.  m = write(fd, addr, n);

    если( ФАЙЛ[fd] не открыт на запись) то вернуть (-1);
    если(n == 0) то вернуть 0;
    если( ФАЙЛ[fd] открыт на запись с флагом O_APPEND ) то
      RWptr = длина_файла; /* т.е. встать на конец файла */
    если( RWptr > длина_файла ) то
      заполнить нулями байты файла в интервале
      ФАЙЛ[fd][ длина_файла..RWptr-1 ] = '\0';
    скопировать байты из памяти процесса в файл
      ФАЙЛ[fd][ RWptr..RWptr+n-1 ] = addr[ 0..n-1 ];
      отводя на диске новые блоки, если надо
    RWptr += n;
    если( RWptr > длина_файла ) то
          длина_файла = RWptr;
    вернуть n;

4.2.2.  m = read(fd, addr, n);

    если( ФАЙЛ[fd] не открыт на чтение) то вернуть (-1);
    если( RWptr >= длина_файла ) то вернуть 0;
    m = MIN( n, длина_файла - RWptr );
    скопировать байты из файла в память процесса
      addr[ 0..m-1 ] = ФАЙЛ[fd][ RWptr..RWptr+m-1 ];
    RWptr += m;
    вернуть m;

4.3.  Найдите ошибки в фрагменте программы:

    #define STDOUT 1  /* дескриптор стандартного вывода */
    int i;
    static char s[20] = "hi\n";
    char c = '\n';
    struct a{ int x,y; char ss[5]; } po;

    scanf( "%d%d%d%s%s", i, po.x, po.y, s, po.ss);
    write( STDOUT, s, strlen(s));
    write( STDOUT, c, 1 );       /* записать 1 байт */

Ответ: в функции scanf перед аргументом i должна стоять операция "адрес", то есть &i.
Аналогично про &po.x и &po.y.  Заметим, что s - это массив, т.е.  s и так есть адрес,
поэтому перед s операция & не нужна; аналогично про po.ss - здесь & не требуется.
     В системном вызове write второй аргумент должен быть адресом данного, которое мы
хотим записать в файл.  Поэтому мы должны были написать &c (во втором вызове write).
     Ошибка в scanf - указание  значения  переменной  вместо  ее  адреса  -  является
довольно распространенной и не может быть обнаружена компилятором (даже при использо-
вании прототипа функции scanf(char *fmt, ...), так как scanf - функция  с  переменным

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

числом аргументов заранее не определенных типов). Приходится полагаться исключительно
на собственную внимательность!

4.4.  Как по дескриптору файла узнать, открыт он на чтение, запись, чтение  и  запись
одновременно? Вот два варианта решения:

    #include 
    #include 
    #include  /* там определено NOFILE */
    #include 

    char *typeOfOpen(fd){
      int flags;
      if((flags=fcntl (fd, F_GETFL, NULL)) < 0 )
        return NULL;  /* fd вероятно не открыт */
      flags &= O_RDONLY | O_WRONLY | O_RDWR;
      switch(flags){
      case O_RDONLY:  return "r";
      case O_WRONLY:  return "w";
      case O_RDWR:    return "r+w";
      default:        return NULL;
      }
    }

    char *type2OfOpen(fd){
      extern errno; /* см. главу "системные вызовы" */
      int r=1, w=1;
      errno = 0; read(fd, NULL, 0);
      if( errno == EBADF ) r = 0;
      errno = 0; write(fd, NULL, 0);
      if( errno == EBADF ) w = 0;
      return (w && r) ? "r+w" :
              w       ? "w"   :
              r       ? "r"   :
                        "closed";
    }

    main(){
      int i; char *s, *p;
      for(i=0; i < NOFILE; i++ ){
     s = typeOfOpen(i); p = type2OfOpen(i);

        printf("%d:%s %s\n", i, s? s: "closed", p);
      }
    }

Константа NOFILE означает максимальное число одновременно открытых файлов для  одного
процесса  (это размер таблицы открытых процессом файлов, таблицы дескрипторов).  Изу-
чите описание системного вызова fcntl (file control).

4.5.  Напишите функцию rename() для переименования файла.  Указание: используйте сис-
темные вызовы link() и unlink().  Ответ:

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

       rename( from, to )
         char *from,     /* старое имя */
              *to;       /* новое имя  */
       {
           unlink( to );   /* удалить файл to    */
           if( link( from, to ) < 0 ) /* связать */
               return (-1);
           unlink( from ); /* стереть старое имя */
           return 0;       /* OK */
       }

Вызов
     link(существующее_имя, новое_имя);
создает файлу альтернативное имя - в UNIX файл может иметь несколько имен: так каждый
каталог  имеет  какое-то  имя  в родительском каталоге, а также имя "." в себе самом.
Каталог же, содержащий подкаталоги, имеет некоторое имя в  своем  родительском  ката-
логе, имя "." в себе самом, и по одному имени ".." в каждом из своих подкаталогов.
     Этот вызов будет неудачен, если файл новое_имя уже существует; а также  если  мы
попытаемся создать альтернативное имя в другой файловой системе.  Вызов
     unlink(имя_файла)
удаляет имя файла. Если файл больше не имеет имен - он уничтожается. Здесь есть  одна
тонкость: рассмотрим фрагмент

    int fd;
    close(creat("/tmp/xyz", 0644)); /*Создать пустой файл*/
    fd = open("/tmp/xyz", O_RDWR);
    unlink("/tmp/xyz");
            ...
    close(fd);

Первый оператор создает пустой файл.   Затем  мы  открываем  файл  и  уничтожаем  его
единственное  имя.  Но поскольку есть программа, открывшая этот файл, он не удаляется
немедленно!  Программа далее работает с безымянным файлом при помощи дескриптора  fd.
Как  только  файл  закрывается  -  он будет уничтожен системой (как не имеющий имен).
Такой трюк используется для создания временных рабочих файлов.
     Файл можно удалить из каталога только в том случае, если  данный  каталог  имеет
для вас код доступа "запись".  Коды доступа самого файла при удалении не играют роли.
     В современных версиях UNIX есть системный вызов rename,  который  делает  то  же
самое, что и написанная нами одноименная функция.

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

    /* Эта программа компилируется в a.out */
    main(){
        int fd = creat("zz.out", 0644);
        write(fd, "It's me\n", 8);
    }

Мы же хотим получить вывод на терминал, а не в файл. Очевидно, мы должны сделать файл
zz.out синонимом устройства /dev/tty (см. конец этой главы). Это можно сделать коман-
дой ln:

    $ rm zz.out ; ln /dev/tty zz.out
    $ a.out
    $ rm zz.out

или программно:

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

    /* Эта программа компилируется в start */
    /* и вызывается  вместо a.out          */
    #include 
    main(){
       unlink("zz.out");
       link("/dev/tty", "zz.out");
         if( !fork()){ execl("a.out", NULL); }
         else wait(NULL);
       unlink("zz.out");
    }

(про fork, exec, wait смотри в главе про UNIX).
     Еще один пример: программа a.out желает запустить программу /usr/bin/vi  (смотри
про функцию system() сноску через несколько страниц):

    main(){
       ... system("/usr/bin/vi xx.c"); ...
    }

На вашей же машине редактор vi помещен в /usr/local/bin/vi.  Тогда вы просто создаете
альтернативное имя этому редактору:

    $ ln /usr/local/bin/vi /usr/bin/vi

Помните, что альтернативное имя файлу можно создать лишь в той же  файловой  системе,
где содержится исходное имя. В семействе BSD [*] это ограничение можно  обойти,  создав
Предыдущая страница Следующая страница
1 ... 19 20 21 22 23 24 25  26 27 28 29 30 31 32 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама