Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование в Unix-системах на Си Выпуск 3


Служба Рассылок Subscribe.Ru

ПРОГРАММИРОВАНИЕ В UNIX-СИТЕМАХ

Выпуск No. 3 от 3 января 2002 г.


1.6 Обработка ошибок

Когда происходит ошибка в одной из функций Unix, часто возвращается отрицательное значение и целая переменная errno обычно устанавливается в значение, которое дает дополни-тельную информацию. Например, функция open возвращает либо неотрицательный дескриптор если все OK, либо -1, если произошла ошибка. В случае ошибки от open, существует 15 раз-личных значений errno (файл не существует, проблемы доступа и т.д.)

Файл errno.h определяет переменную errno и константы для каждого значения которое может принимать errno. Каждая из этих констант начинается с символа E. Например, если errno равно константе EACCES, это указывает на проблемы доступа (мы не имеем прав открыть файл и т.д.). Стандарт POSIX определяет errno как

extern int errno;

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

Что бы помочь вывести сообщение об ошибке, Си стандартом определены две функции.

#include <string.h>
char *strerror(int errnum);
Return: указатель на строку сообщения

Эта функция отображает errnum (которое обычно равно значение errno) на строку сообщения об ошибке и возвращает указатель на эту строку.

Функция perror выдает сообщение об ошибке на стандартный вывод ошибки (основанный на текущем значении errno).

#include <stdio.h>
void perror(const char *msg);

Сначала, она выводит стоку на которую указывает msg, затем двоеточие и пробел, за ни-ми следует сообщение об ошибке, соответствующее значению errno и наконец символ новой строки.

Программа 1.6

#include <errno.h>
int main(int argc, char *argv[])
{
   fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
   errno = ENOENT;
   perror (argv[0]) ;
   exit(0) ;
}

Если мы скомпилируем программу в файл a.out, мы имеем:

$a.out
EACCES: Permission denied
a.out: No such file  or directory

Заметим, что мы передаем имя программы (argv[0], чье значение - a.out) в качестве ар-гумента perror. Это стандартное Unix соглашение. Делая подобным образом, если программа выполняется как часть конвейера, как в

prog1 < inputfile | prog2 | prog3 > outputfile

мы сможем сказать, какая из трех программ генерирует сообщение об ошибке.

1.7 Идентификация пользователя

ID пользователя

Пользовательский ID из нашей записи в файле паролей это числовое значение которое идентифицирует нас системе. Этот ID присоединяется системным администратором , когда нам дает аккаунт и мы не можем его уже изменить. Каждый пользователь имеет уникальный ID. Мы увидим как ID пользователя используется ядром для проверки того имеем ли мы соответствующие права доступа для выполнения определенных операций.

Мы называем пользователя чей ID = 0 либо root или superuser. Запись в файле паролей обычно имеет имя регистрации как root и этот пользователь имеет все привилегии в системе.

Программа 1.7 печатает ID пользователя и ID группы.

Программа 1.7

#include <stdio.h>
#include <unistd.h>

int main(void) {
   printf("uid = %d, gid = %d\n", getuid(), getgid());
   exit(0);
}

Мы вызываем функции getuid и getgid чтобы выяснить ID пользователя и ID группы. За-пустив программу, получим следующее:

$ a.out
uid = 224, gid = 20

Групповой ID

Наша запись в файле паролей также указывает наш групповой ID. Этот групповой ID также присоединяется системным администратором.

Обычно существуют множество записей в файле паролей которые указывают на один и тот же групповой ID. Группы обычно используются в Unix, чтобы собрать вместе пользователей, работающих над одним проектом или в одном отделе.

Также существует файл группы который отображает имена группы в числовые иденти-фикаторы, так называемый групповой ID. Обычно это файл /etc/group.

1.8 Сигналы

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

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

Множество условий генерируют сигналы. Существуют два терминальных ключа, назы-ваемых ключ прерывания (часто клавиша DELETE или CTRL+C) и ключ выхода (часто CTRL+Backslash). Они используются для прерывания текущей задачи. Но существует одно ог-раничение: мы должны владеть процессом, чтобы послать ему сигнал.

Модифицируем программу 1.5, так чтобы она могла обрабатывать клавишу прерывания (CTRL+C). Раньше, при выполнении программы 1.5, если мы нажмем CTRL+C, процесс завер-шается. Это реакция на сигнал по умолчанию. Процесс ничего не сказал ядру о том, что необ-ходимо обработать такой сигнал как-нибудь иначе, поэтому процесс завершается.

Теперь мы перехватываем сигнал, указывая имя функции, которая будет вызываться, ко-гда будет сгенерирован сигнал SIGINT. Такой сигнал будет сгенерирован ядром когда мы на-жмем CTRL+C.

Программа 1.8

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "errorx.h"

#define MAXLINE 4096

static void sig_int(int);

int main ()
{
  char  buf[MAXLINE];
  pid_t pid;
  int   status;

  if(signal(SIGINT, sig_int) == SIG_ERR)
     err_sys("signal error");

  printf("%% ");
  while (fgets(buf,MAXLINE,stdin)!=NULL) {
    buf[strlen(buf)-1]=0;    // replece newline with null

    if((pid=fork()) <0)
        err_sys("fork error");
    else if(pid==0) {         /* child */
         execlp(buf,buf, (char *)0);
         err_ret("couldn't execute: %s", buf);
  exit(127);
      }
    /* parent */
    if( (pid=waitpid(pid, &status, 0))<0)
 err_sys("waitpid error");
    printf("%% ");
   }

 exit(0);
}

void sig_int(int signo)
{
  printf("interrupt\n%% ");
}

Функция обработки сигнала называется sig_int и когда приходит сигнал, она просто печатает сообщение.


Ведущий рассылки  Астероид

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное