Главная · Поиск книг · Поступления книг · 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 ... 35 36 37 38 39 40 41  42 43 44 45 46 47 48 ... 87
    fsaved = signal (sig, SIG_IGN);
       sys_call(...);
    signal (sig, fsaved);

или так:

    sighold(sig);
       sys_call(...);
    sigrelse(sig);

Сигналами могут быть прерваны не все системные вызовы и не при всех обстоятельствах.

6.4.3.  Напишите функцию sleep(n), задерживающую выполнение программы  на  n  секунд.
Воспользуйтесь  системным  вызовом  alarm(n)  (будильник)  и вызовом pause(), который
задерживает программу до получения любого сигнала.  Предусмотрите рестарт при получе-
нии  во  время ожидания другого сигнала, нежели SIGALRM. Сохраняйте заказ alarm, сде-
ланный до вызова sleep (alarm выдает число секунд, оставшееся до завершения  предыду-
щего заказа).  На самом деле есть такая СТАНДАРТНАЯ функция.  Ответ:

ненужной строки) и последующего его переименования.
А. Богатырев, 1992-95                  - 218 -                              Си в UNIX

    #include 
    #include 
    #include 

    int got;   /* пришел ли сигнал */

    void onalarm(int sig)
    { printf( "Будильник\n" ); got++; } /* сигнал получен */

    void sleep(int n){
       time_t time(), start = time(NULL);
       void (*save)();
       int oldalarm, during = n;

       if( n <= 0 ) return;
       got = 0;
       save = signal(SIGALRM, onalarm);
       oldalarm = alarm(3600); /* Узнать старый заказ */
       if( oldalarm ){
           printf( "Был заказан сигнал, который придет через %d сек.\n",
                    oldalarm );
           if(oldalarm > n) oldalarm -= n;
           else { during = n = oldalarm; oldalarm = 1; }
       }
       printf( "n=%d oldalarm=%d\n", n, oldalarm );
       while( n > 0 ){
         printf( "alarm(%d)\n", n );
         alarm(n);  /* заказать SIGALRM через n секунд */

         pause();

         if(got) break;
         /* иначе мы сбиты с pause другим сигналом */
         n = during - (time(NULL) - start); /* прошло времени */
       }
       printf( "alarm(%d) при выходе\n", oldalarm );
       alarm(oldalarm);  /* alarm(0) - отмена заказа сигнала */
       signal(SIGALRM, save); /* восстановить реакцию */
    }

    void onintr(int nsig){
       printf( "Сигнал SIGINT\n"); signal(SIGINT, onintr);
    }

    void onOldAlarm(int nsig){
       printf( "Звонит старый будильник\n");
    }

    void main(){
            int time1 = 0;  /* 5, 10, 20 */
            setbuf(stdout, NULL);
            signal(SIGINT, onintr);
            signal(SIGALRM, onOldAlarm); alarm(time1);
                        sleep(10);
            if(time1) pause();
            printf("Чао!\n");
    }

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

6.4.4.  Напишите "часы", выдающие текущее время каждые 3 секунды.

    #include 
    #include 
    #include 
    void tick(nsig){
       time_t tim; char *s;
       signal (SIGALRM, tick);
       alarm(3); time(&tim);
       s = ctime(&tim);
       s[ strlen(s)-1 ] = '\0'; /* обрубить '\n' */
       fprintf(stderr, "\r%s", s);
    }
    main(){ tick(0);
            for(;;) pause();
    }

6.5.  Жизнь процессов.

6.5.1.  Какие классы памяти имеют данные, в каких сегментах  программы  они  располо-
жены?

            char x[] = "hello";
            int y[25];
            char *p;
            main(){
                    int z = 12;
                    int v;
                    static int w = 25;
                    static int q;
                    char s[20];
                    char *pp;
                    ...
                    v = w + z;      /* #1 */
            }

Ответ:

    Переменная  Класс памяти     Сегмент   Начальное значение
         x        static        data/DATA      "hello"
         y        static        data/BSS     {0, ..., 0}
         p        static        data/BSS        NULL
         z        auto          stack            12
         v        auto          stack        не определено
         w        static        data/DATA        25
         q        static        data/BSS          0
         s        auto          stack        не определено
         pp       auto          stack        не определено
         main     static        text/TEXT

Большими буквами обозначены сегменты, хранимые в выполняемом файле:
DATA - это инициализированные статические данные (которым присвоены начальные  значе-
     ния).  Они помещаются компилятором в файл в виде готовых констант, а при запуске
     программы (при ее загрузке в память  машины),  просто  копируются  в  память  из
     файла.
BSS (Block Started by Symbol)
     - неинициализированные статические данные. Они по умолчанию имеют начальное зна-
     чение  0  (NULL,  "",  '\0').  Эта память расписывается нулями при запуске прог-
     раммы, а в файле хранится лишь ее размер.

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

TEXT - сегмент, содержащий машинные команды (код).

Хранящаяся в файле выполняемая программа имеет также заголовок - в  нем  в  частности
содержатся  размеры  перечисленных  сегментов  и их местоположение в файле; и еще - в
самом конце файла - таблицу имен.  В ней содержатся имена всех функций и  переменных,
используемых  в  программе,  и  их адреса. Эта таблица используется отладчиками adb и
sdb, а также при сборке программы  из  нескольких  объектных  файлов  программой  ld.
Просмотреть ее можно командой
     nm имяФайла
Для экономии дискового пространства эту таблицу часто удаляют, что делается командой
     strip имяФайла
Размеры сегментов можно узнать командой
     size имяФайла
Программа, загруженная в память компьютера (т.е. процесс), состоит из  3x  сегментов,
относящихся непосредственно к программе:
stack
     - стек для локальных переменных функций (автоматических переменных).  Этот  сег-
     мент  существует  только у выполняющейся программы, поскольку отведение памяти в
     стеке производится выполнением некоторых машинных команд (поэтому описание авто-
     матических  переменных в Си - это на самом деле выполняемые операторы, хотя и не
     с точки зрения языка).  Сегмент стека автоматически растет  по  мере  надобности
     (если мы вызываем новые и новые функции, отводящие переменные в стеке).  За этим
     следит аппаратура диспетчера памяти.
data - сегмент, в который склеены сегменты статических данных DATA и BSS, загруженные
     из  файла.   Этот  сегмент  также может изменять свой размер, но делать это надо
     явно - системными вызовами sbrk или brk. В частности, функция malloc() для  раз-
     мещения динамически отводимых данных увеличивает размер этого сегмента.
text - это выполняемые команды, копия сегмента TEXT из файла.  Так строка с меткой #1
     содержится в виде машинных команд именно в этом сегменте.

Кроме того, каждый процесс имеет еще:

proc - это резидентная часть паспорта процесса в таблице процессов в ядре  операцион-
     ной системы;
user - это 4-ый сегмент процесса - нерезидентная часть паспорта  (u-area).   К  этому
     сегменту имеет доступ только ядро, но не сама программа.

Паспорт процесса был поделен на 2 части только из соображений экономии памяти в ядре:
контекст  процесса (таблица открытых файлов, ссылка на I-узел текущего каталога, таб-
лица реакций на сигналы, ссылка на I-узел управляющего терминала, и.т.п.) нужен  ядру
только  при обслуживании текущего активного процесса.  Когда активен другой процесс -
эта информация в памяти ядра не нужна.  Более того, если процесс из-за нехватки места
в памяти машины был откачан на диск, эта информация также может быть откачана на диск
и подкачана назад лишь вместе с процессом.  Поэтому контекст был выделен в  отдельный
сегмент,  и сегмент этот подключается к адресному пространству ядра лишь при выполне-
нии процессом какого-либо системного вызова (это подключение называется "переключение
контекста"  - context switch).  Четыре сегмента процесса могут располагаться в памяти
машины не обязательно подряд - между ними могут лежать сегменты других процессов.
     Схема составных частей процесса:

            П  Р  О  Ц  Е  С  С
      таблица процессов:
      паспорт  в ядре       сегменты в памяти

       struct proc[]
            ####---------------> stack      1
            ####                 data       2
                                 text       3
                   контекст:   struct user  4

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

Каждый процесс имеет уникальный номер, хранящийся в поле p_pid в структуре proc[*].   В
ней  также  хранятся:  адреса  сегментов процесса в памяти машины (или на диске, если
процесс откачан); p_uid - номер владельца процесса; p_ppid - номер процесса-родителя;
p_pri,  p_nice  -  приоритеты процесса; p_pgrp - группа процесса; p_wchan - ожидаемое
процессом событие; p_flag и p_stat - состояние процесса; и многое  другое.  Структура
proc определена в include-файле , а структура user - в .

6.5.2.  Системный вызов fork() (вилка) создает новый процесс: копию процесса,  издав-
шего вызов.  Отличие этих процессов состоит только в возвращаемом fork-ом значении:

    0                   - в новом процессе.
    pid нового процесса - в исходном.

Вызов fork может завершиться неудачей если таблица процессов переполнена.  Простейший
способ сделать это:

    main(){
          while(1)
            if( ! fork()) pause();
    }

Одно гнездо таблицы процессов зарезервировано - его может использовать только  супер-
пользователь  (в  целях  жизнеспособности  системы: хотя бы для того, чтобы запустить
программу, убивающую все эти процессы-варвары).
     Вызов fork создает копию всех 4х сегментов процесса и выделяет порожденному про-
цессу  новый паспорт и номер.  Иногда сегмент text не копируется, а используется про-
цессами совместно ("разделяемый сегмент") в целях экономии  памяти.  При  копировании
сегмента  user  контекст порождающего процесса наследуется порожденным процессом (см.
ниже).
     Проведите опыт, доказывающий что порожденный системным вызовом fork() процесс  и
породивший его - равноправны. Повторите несколько раз программу:

    #include 
    int pid, i, fd; char c;
    main(){
       fd = creat( "TEST", 0644);
       if( !(pid = fork())){ /* сын: порожденный процесс */
               c = 'a';
               for(i=0; i < 5; i++){
                 write(fd, &c, 1); c++; sleep(1);
               }
               printf("Сын %d окончен\n", getpid());
               exit(0);
       }
       /* else процесс-отец */
       c = 'A';
       for(i=0; i < 5; i++){
               write(fd, &c, 1); c++; sleep(1);
       }
       printf("Родитель %d процесса %d окончен\n",
               getpid(), pid );
    }

В файле TEST мы будем от случая к случаю получать строки вида

    aABbCcDdEe  или  AaBbcdCDEe

что говорит о том, что первым "проснуться" после fork() может любой из  двух  процес-
сов. Если же опыт дает устойчиво строки, начинающиеся с одной и той же буквы - значит
____________________
   [*] Процесс может узнать его вызовом pid=getpid();

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

в данной реализации системы один из процессов все же запускается раньше. Но не  стоит
использовать этот эффект - при переносе на другую систему его может не быть!
     Данный опыт основан на следующем свойстве системы  UNIX:  при  системном  вызове
fork()  порожденный процесс получает все открытые порождающим процессом файлы "в нас-
ледство" - это соответствует тому, что таблица открытых процессом файлов копируется в
процесс-потомок.   Именно  так,  в  частности,  передаются от отца к сыну стандартные
каналы 0, 1, 2: порожденному процессу не нужно открывать стандартные  ввод,  вывод  и
вывод  ошибок  явно.  Изначально  же они открываются специальной программой при вашем
Предыдущая страница Следующая страница
1 ... 35 36 37 38 39 40 41  42 43 44 45 46 47 48 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама