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

Программирование в Unix-системах на Си


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

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

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


1. Введение

Все операционный системы предлагают службы для программ когда последние выпол-няются. Типичные службы выполняют новую программу, открывают файл, читают файл, рас-пределяют память, получают текущее время и т.д.

Целью сегодняшнего выпуска будет ознакомление со службами предоставляемыми раз-личными UNIX операционными системами.

1.1 Регистрация

Входное имя

Когда мы регистрируемся в системе UNIX мы вводим наше входное имя, за которым следует пароль. Наше регистрационное имя ищется в системном файле паролей, обычно /etc/passwd. Если мы посмотрим на нашу строку с файле паролей мы увидим, что она состоит их семи полей, разделенных двоеточиями: наше входное имя, шифрованный пароль, числовой пользовательский идентификатор (UID), групповой идентификатор (GID), поле комментария, домашняя директория (/home/baba_yaga) и программный шелл (/bin/bash).

Многие новые системы помещают шифрованные пароли с отдельный файл.

Шеллы

Как только мы зарегистрировались, возможно отображается некоторая служебная ин-формация и затем мы можем вводить команды шеллу. Шелл (shell) - это интерпретатор ко-мандной строки который принимает входные данные пользователя и выполняет команды. Пользователь вводит данный шеллу обычно через терминал или иногда из файла. В этом случае такой файл называется скриптом.

Наиболее известные шеллы:

  • Bourne shell (/bin/sh)
  • C shell (/bin/csh)
  • Korn shell (/bin/ksh)

Система знает какой шелл выполнять из файла паролей.

1.2 Файлы и директории

Файловая система

Файловая система UNIX это иерархическое расположение директориев и файлов. Все начитается с директории, называемой корневой, чье имя обозначается единственным символом / (прямой слэш).

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

Имя файла

Имена в директории называются именами файлов. Существуют только два сим-вола которые не могут присутствовать в имени файла - это прямой слэш (/) и нуль-символ. Слэш разделяет имена файлов которые формируют путевое имя, а нуль-символ завершает путевое имя.

Когда создается новая директория, автоматически создаются два имени файла, называемые . (точка) и ..(точка-точка). Точка ссылается на текущий директорий, а точка-точка ссылается на родительский директорий. В самой отдаленной родительской дирек-тории, т.е в корневой, точка-точка это тоже самое что и точка.

Путевое имя

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

Получить список имен файлов в директории не так уж и трудно. Вот небольшая про-граммка которое делает это. По сути это укороченная версия команды ls

/*
Файл myls.c
*/
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include "errorx.h"
int main (int argc, char *argv[])
{
  DIR *dp;
  struct dirent *dirp;

  if(argc !=2)
     err_quit("a single argument (the directory name) is required");
  if( (dp=opendir(argv[1]))==NULL)
     err_sys("can't open %s", argv[1]);

  while( (dirp = readdir(dp))!=NULL)
    printf("%s\n", dirp->d_name);
  closedir(dp);

  exit(0);
}
/* Файл errorx.h */
#define MAX_LINE 4096
void err_quit(const char *, ...);
void err_sys(const char *,...);

И для компиляции вам понадобится файл в котором реализованы функции обработки ошибок. Вот он:

/* Файл errorx.c */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "errorx.h"

static void err_doit(int, const char *, va_list);

char  *pname=NULL;

/* Фатальная ошибка связанная с системным вызовом. Печатаем сообщение и завершаем работу */
void err_sys(const char *fmt, ...)
{
  va_list ap;

  va_start(ap,fmt);
  err_doit(1,fmt,ap);
  va_end(ap);

  exit(1);
}

/* Фатальная ошибка не связанная с системным вызовом. Печатаем сообщение и завер-шаем работу */
void err_quit(const char *fmt, ...)
{
  va_list  ap;

  va_start(ap, fmt);
  err_doit(0,fmt,ap);
  va_end(ap);

  exit(1);
}

/* Печатаем сообщение и возвращаемся в вызвавшую функцию */
static void err_doit(int errnoflag, const char *fmt, va_list ap)
{
  int errno_save;
  char buf[MAX_LINE];

  errno_save=errno;
  vsprintf(buf,fmt,ap);
  if(errnoflag)
      sprintf(buf+strlen(buf), ": %s", strerror(errno_save));
  strcat(buf, "\n");
  fflush(stdout);
  fputs(buf,stderr);
  fflush(NULL);

  return;
}

Последние два файла будут нам необходимы почти всегда. По мене необходимости мы будем добавлять в них описания, поэтому сохраните их в надежном месте.

Чтобы скомпилировать программу выполните следующее:

$ gcc - o myls myls.c errorx.c

Знак $ это приглашение оболочки в UNIX вводить команды. Мы используем компилятор gcc. Параметр -o указывает компилятору поместить исполняемый модуль в файл, в нашем случае это myls

Дальше мы можем выполнить только что созданную программу:

$ myls  /dev
 .
 ..
MAKEDEV
Console
Tty
Mem
Null
$ myls /var/spool/mqueue
can't open /var/spool/mqueue: Permission denied

Вначале мы включили наш собственный заголовокerrorx.h, в котором описаны прототи-пы функций обработки ошибок с переменным числом аргументов. Файл errorx.c     содержит код этих функций.

Мы принимаем аргументы командной строки, argv[1], как имя директории для которой надо получить список. Для манипуляции с директорием мы используем функции opendir, readdir и closedir.

Функция opendir возвращает указатель на структуру DIR, и мы передаем этот указатель фукнции readdir. Нам не надо пока заботиться о том, что содержит эта структура. Затем мы в цикле вызываем функцию readdir, чтобы прочитать каждую запись директория. Она возвраща-ет указатель на структуру dirent или нуль когда дойдет до конца. Все что мы смотрим в струк-туре dirent это имя каждой записи директория (d_name), т.е. по сути имя файла.

Рабочая директория

Каждый процесс имеет рабочую директорию. Это директорий с которого интерпретиру-ются все относительные путевые имена. Процесс может изменить свою рабочую директорию с помощью функции chdir.

Например, относительное путевое имя doc/memo/joe ссылается на файл или директорий joe, в директории memo, в директории doc, которая должна быть директорием в рабочей директории.

1.3 Ввод и вывод

Файловые дескрипторы

Файловый дескриптор это малое неотрицательное целое которое ядро использует чтобы идентифицировать файлы к которым осуществляется доступ определенными пользователями. Всякий раз когда ядро открывает существующий файл или создает новый файл, оно возвращает файловый дескриптор который мы используем когда хотим прочитать или записать файл.

Стандартный ввод, вывод и стандартная ошибка

Условно, все шеллы открывают три дескриптора всякий раз когда запускается новая программа: стандартный ввод, стандартный вывод и стандартная ошибка. Все три дескриптора присоединяются к нашему терминалу. Большинство шеллов дают способ перенаправить любой или все три дескриптора в любой файл. Например

ls > file.list

выполняет команду ls, а стандартный вывод перенаправляется в файл file.list

Не буферизированный ввод/вывод

Не буферизированный ввод/вывод обеспечивается такими функциями как open, read, write, lseek, close. Все эти функции работают с файловыми дескрипторами.

Программа 1.2

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

#define BUFFSIZE  8192
int main()
{
  int n;
  char buf[BUFFSIZE];

  while( (n=read(STDIN_FILENO,buf,BUFFSIZE))>0)
     if(write(STDOUT_FILENO,buf,n)!=n)
          err_sys("write error");
  if(n<0)
     err_sys("read error");
  exit(0);
}

Две константы STDIN_FILENO и STDOUT_FILENO определены в заголовке unistd.h и указывают на файловые дескрипторы стандартного ввода и стандартного вывода. Эти значения обычно равны 0 и 1.

Функция read возвращает количество байт которое мы прочитали и это значение используется для записи. Когда встретиться коней файла, функция read возвратит 0 и программа завершает работу.

Стандартный ввод/вывод

Функции стандартного ввода/вывода предоставляют буферизированный интерфейс к не буферизированным функциям ввода/вывода. Функции стандартного ввода/вывода описаны в заголовочном файле stdio.h

Программа 1.3

#include <stdio.h>
#include "errorx.h"

int main()
{
  int c;
  while ((c=getc(stdin)) !=EOF)
     if(putc(c,stdout) == EOF)
 err_sys("output error");
     if(ferror(stdin))
 err_sys("input error");
   exit(0);
}

Функция getc читает один символ за раз, и этот символ записывается с помощью функ-ции putc. После того как прочитан последний байт, getc возвращает константу EOF. Стандарт-ные константы stdin и stdout также определены в заголовке stdio.h и ссылаются на стандартный ввод и стандартный вывод.


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

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

В избранное