Главная · Поиск книг · Поступления книг · 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 ... 29 30 31 32 33 34 35  36 37 38 39 40 41 42 ... 87
тые  в  данной системе, для чего она просто включает ряд стандартных include-файлов с
описанием этих форматов.  Имена этих файлов также можно отнести к интерфейсу системы.

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

Сам термин "системный вызов" как раз означает "вызов системы  для  выполнения  дейст-
вия",  т.е.  вызов функции в ядре системы.  Ядро работает в привелегированном режиме,
в котором имеет доступ к некоторым системным таблицам[**], регистрам  и  портам  внешних
устройств и диспетчера памяти, к которым обычным программам доступ аппаратно запрещен
(в отличие от MS DOS, где все таблицы ядра доступны пользовательским программам,  что
создает раздолье для вирусов).  Системный вызов происходит в 2 этапа: сначала в поль-
зовательской программе вызывается библиотечная функция-"корешок", тело которой  напи-
сано на ассемблере и содержит команду генерации программного прерывания.  Это - глав-
ное отличие от нормальных Си-функций - вызов по прерыванию.  Вторым  этапом  является
реакция ядра на прерывание:
1.   переход в привелегированный режим;
2.   разбирательство, КТО обратился к ядру, и подключение  u-area  этого  процесса  к
     адресному пространству ядра (context switching);
3.   извлечение аргументов из памяти запросившего процесса;
4.   выяснение, ЧТО же хотят от ядра (один из аргументов, невидимый нам -  это  номер
     системного вызова);
5.   проверка корректности остальных аргументов;
6.   проверка прав процесса на допустимость выполнения такого запроса;
7.   вызов тела требуемого системного вызова - это обычная Си-функция в ядре;
8.   возврат ответа в память процесса;
9.   выключение привелегированного режима;
10.  возврат из прерывания.

     Во время системного вызова (шаг 7) процесс может "заснуть", дожидаясь некоторого
события (например, нажатия кнопки на клавиатуре).  В это время ядро передаст управле-
ние другому процессу. Когда наш процесс будет "разбужен"  (событие  произошло)  -  он
продолжит выполнение шагов системного вызова.
     Большинство системных вызовов возвращают в программу в качестве своего  значения
признак  успеха: 0 - все сделано, (-1) - сисвызов завершился неудачей; либо некоторое
содержательное значение при успехе (вроде дескриптора файла в open(), и (-1) при неу-
даче.   В  случае неудачного завершения в предопределенную переменную errno заносится
номер ошибки, описывающий причину неудачи  (коды  ошибок  предопределены,  описаны  в
include-файле    и имеют вид Eчтото).  Заметим, что при УДАЧЕ эта переменная
просто не изменяется и может содержать любой мусор, поэтому проверять ее имеет  смысл
лишь в случае, если ошибка действительно произошла:

    #include       /* коды ошибок */
    extern int errno;
    extern char *sys_errlist[];
    int value;
    if((value = sys_call(...)) < 0 ){
       printf("Error:%s(%d)\n", sys_errlist[errno],
                                errno );
       exit(errno); /* принудительное завершение программы */
    }

____________________
     Поведение всех программ в системе вытекает из поведения системных вызовов, кото-
рыми  они  пользуются. Даже то, что UNIX является многозадачной системой, непосредст-
венно вытекает из наличия системных вызовов fork, exec, wait и спецификации их  функ-
ционирования!
     То же можно сказать про язык Си - мобильность программы зависит  в  основном  от
набора используемых в ней библиотечных функций (и, в меньшей степени, от диалекта са-
мого языка, который должен удовлетворять стандарту на язык Си).  Если две разные сис-
темы  предоставляют  все  эти  функции (которые могут быть по-разному реализованы, но
должны делать одно и то же), то программа будет компилироваться и  работать  в  обоих
системах, более того, работать в них одинаково.
   [**] Таким как таблица процессов, таблица открытых файлов (всех вместе и для  каждого
процесса), и.т.п.

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

Предопределенный массив sys_errlist, хранящийся в  стандартной  библиотеке,  содержит
строки-расшифровку  смысла  ошибок  (по-английски).  Посмотрите описание функции per-
ror().

6.1.  Файлы и каталоги.

6.1.1.  Используя системный вызов stat, напишите программу, определяющую  тип  файла:
обычный файл, каталог, устройство, FIFO-файл.  Ответ:

    #include 
    #include 

    typeOf( name ) char *name;
    {  int type; struct stat st;
       if( stat( name, &st ) < 0 ){
               printf( "%s не существует\n", name );
               return 0;
       }
       printf("Файл имеет %d имен\n", st.st_nlink);

       switch(type = (st.st_mode & S_IFMT)){
       case S_IFREG:
            printf( "Обычный файл размером %ld байт\n",
                       st.st_size ); break;
       case S_IFDIR:
               printf( "Каталог\n" );      break;
       case S_IFCHR:   /* байтоориентированное  */
       case S_IFBLK:   /* блочноориентированное */
               printf( "Устройство\n" );   break;
       case S_IFIFO:
               printf( "FIFO-файл\n" );    break;
       default:
               printf( "Другой тип\n" );   break;
       }       return type;
     }

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

    struct stat st; int used, fd;
    for(fd=0; fd < NOFILE; fd++ ){
      used = fstat(fd, &st) < 0 ? 0 : 1;
      ...
    }

Программа может  использовать  дескрипторы  файлов  с  номерами  0..NOFILE-1  (обычно
0..19).  Если fstat для какого-то fd вернул код ошибки (<0), это означает, что данный
дескриптор не связан с открытым файлом (т.е. не используется).  NOFILE  определено  в
include-файле , содержащем разнообразные параметры данной системы.

6.1.3.  Напишите упрощенный аналог команды ls,  распечатывающий  содержимое  текущего
каталога  (файла с именем ".") без сортировки имен по алфавиту.  Предусмотрите чтение
каталога, чье имя задается как аргумент программы.  Имена "." и ".." не выдавать.
     Формат каталога описан в header-файле  и в "канонической" версии выг-
лядит  так:  каталог  - это файл, состоящий из структур direct, каждая описывает одно
имя файла, входящего в каталог:

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

    struct  direct {
       unsigned short d_ino;   /* 2 байта: номер I-узла */
       char    d_name[DIRSIZ]; /* имя файла             */
    };

В семействе BSD формат каталога несколько иной - там записи имеют разную длину, зави-
сящую от длины имени файла, которое может иметь длину от 1 до 256 символов.
     Имя файла может состоять из любых  символов,  кроме  '\0',  служащего  признаком
конца  имени  и  '/', служащего разделителем.  В имени допустимы пробелы, управляющие
символы (но не рекомендуются!), любое число точек (в отличие от MS DOS, где допустима
единственная  точка,  отделяющая  собственно имя от суффикса (расширения)), разрешены
даже непечатные (т.е. управляющие) символы!  Если имя файла имеет длину  14  (DIRSIZ)
символов,  то  оно  не оканчивается байтом '\0'. В этом случае для печати имени файла
возможны три подхода:
1.   Выводить символы при помощи putchar()-а в цикле. Цикл прерывать по индексу  рав-
     ному DIRSIZ, либо по достижению байта '\0'.
2.   Скопировать поле d_name в другое место:

         char buf[ DIRSIZ + 1 ];
         strncpy(buf, d.d_name, DIRSIZ);
         buf[ DIRSIZ ] = '\0';

     Этот способ лучший, если имя файла надо не просто напечатать, но и запомнить  на
     будущее, чтобы использовать в своей программе.
3.   Использовать такую особенность функции printf():

         #include 
         #include 

         struct direct d;
            ...
         printf( "%*.*s\n", DIRSIZ, DIRSIZ, d.d_name );

     Если файл был стерт, то в поле d_ino записи каталога будет содержаться 0 (именно
поэтому  I-узлы нумеруются начиная с 1, а не с 0).  При удалении файла содержимое его
(блоки) уничтожается, I-узел освобождается, но имя в  каталоге  не  затирается  физи-
чески,  а  просто помечается как стертое: d_ino=0; Каталог при этом никак не уплотня-
ется и не укорачивается!  Поэтому имена с d_ino==0 выдавать не следует  -  это  имена
уже уничтоженных файлов.
     При создании нового имени (creat, link, mknod) система просматривает  каталог  и
переиспользует  первый от начала свободный слот (ячейку каталога) где d_ino==0, запи-
сывая новое имя в него (только в этот момент старое имя-призрак окончательно исчезнет
физически).  Если пустых мест нет - каталог удлиняется.
     Любой каталог всегда содержит два стандартных имени: "."  - ссылка  на  этот  же
каталог  (на  его  собственный  I-node),  ".." - на вышележащий каталог.  У корневого
каталога "/" оба этих имени ссылаются на него же самого (т.е. содержат d_ino==2).
     Имя каталога не содержится в нем самом. Оно содержится в "родительском" каталоге
...
     Каталог в UNIX - это обычный дисковый файл. Вы можете читать его из своих  прог-
рамм. Однако никто (включая суперпользователя[**]) не может записывать что-либо в  ката-
лог при помощи write.  Изменения содержимого каталогов выполняет только ядро, отвечая
на запросы в виде системных вызовов creat, unlink, link, mkdir, rmdir, rename, mknod.
Коды  доступа для каталога интерпретируются следующим образом:
w запись
     S_IWRITE.  Означает право создавать и уничтожать в  каталоге  имена  файлов  при
____________________
   [**] Суперпользователь (superuser) имеет uid==0.  Это  "привелегированный"  пользова-
тель,  который имеет право делать ВСЕ. Ему доступны любые сисвызовы и файлы, несмотря
на коды доступа и.т.п.

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

     помощи этих вызовов. То есть: право создавать, удалять и переименовывать файлы в
     каталоге.   Отметим,  что для переименования или удаления файла вам не требуется
     иметь доступ по записи к самому файлу - достаточно  иметь  доступ  по  записи  к
     каталогу, содержащему его имя!
r чтение
     S_IREAD.  Право читать каталог как обычный файл (право  выполнять  opendir,  см.
     ниже):  благодаря  этому  мы  можем  получить список имен файлов, содержащихся в
     каталоге.  Однако, если мы ЗАРАНЕЕ знаем имена файлов в каталоге, мы МОЖЕМ рабо-
     тать с ними - если имеем право доступа "выполнение" для этого каталога!
x выполнение
     S_IEXEC.  Разрешает поиск в  каталоге.  Для  открытия  файла,  создания/удаления
     файла,  перехода  в другой каталог (chdir), система выполняет следующие действия
     (осуществляемые функцией namei() в ядре): чтение каталога и поиск в нем  указан-
     ного  имени  файла  или  каталога;  найденному  имени соответствует номер I-узла
     d_ino; по номеру узла система считывает с диска сам I-узел нужного  файла  и  по
     нему добирается до содержимого файла.  Код "выполнение" - это как раз разрешение
     такого просмотра каталога системой.  Если каталог имеет доступ на  чтение  -  мы
     можем получить список файлов (т.е. применить команду ls); но если он при этом не
     имеет кода доступа "выполнение" - мы не сможем получить доступа ни к  одному  из
     файлов каталога (ни открыть, ни удалить, ни создать, ни сделать stat, ни chdir).
     Т.е. "чтение" разрешает применение вызова read, а "выполнение"  -  функции  ядра
     namei.   Фактически  "выполнение"  означает "доступ к файлам в данном каталоге";
     еще более точно - к I-nodам файлов этого каталога.
t sticky bit
     S_ISVTX - для каталога он означает, что удалить или переименовать некий  файл  в
     данном  каталоге могут только: владелец каталога, владелец данного файла, супер-
     пользователь.  И никто другой. Это исключает удаление файлов чужими.
Предыдущая страница Следующая страница
1 ... 29 30 31 32 33 34 35  36 37 38 39 40 41 42 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама