Содержание


Инструменты ОС Linux для разработчиков приложений для ОС Windows. Часть 13. Библиотеки API POSIX

Comments

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

Несмотря на то, что основной объём API, доступных программисту в языке C (базовый уровень API Linux), формализован в рамках нескольких стандартов POSIX, библиотеки Linux предоставляют некоторые специфические возможности, выходящие за границы стандартов (например, поддержку файловой системы sysfs, которой просто нет в POSIX/UNIX).

Существующие библиотеки API POSIX позволяют переносить программное обеспечение между множеством UNIX-подобных ОС. Наиболее полное и профессиональное описание основных вызовов POSIX, объёмом более чем в 1000 страниц, можно получить в книге У.Р.Стивенcа. Поэтому очевидно, что невозможно подготовить более или менее полный обзор механизмов POSIX в единственной статье, но этого и не требуется, так как на то существуют обстоятельные руководства. Кроме того, большая часть механизмов POSIX уже знакома большинству программистов, пришедших с других платформ, только они ещё не знают, что эти библиотеки принадлежат POSIX, и называют их «стандартными библиотеками языка С». Поэтому значительная часть API POSIX может считаться уже известной и понятной.

Но в этом API существуют ключевые понятия, свойственные только UNIX системам с их традициями. И предлагаемый обзор будет сфокусирован именно на таких принципиально "незнакомых" сторонах POSIX API, которые и вызывают основную трудность в ходе миграции.

Сводный перечень по разделам API

Полный перечень вызовов POSIX API чрезвычайно объёмен, но мы сделаем попытку распределить API по группам. В этом перечне имена функций, которые фундаментальные для UNIX и могут оказаться незнакомыми для Windows-программиста, которые мы обсудим в дальнейшем — выделены жирным шрифтом. Далее мы подробно рассмотрим только этот ограниченный список вызовов, но и этого будет достаточно, чтобы сориентироваться и со всем окружающим их множеством API. Основными группами вызовов POSIX API можно считать:

  1. файловый ввод вывод и дескрипторы файлов;
    open, create, close, lseek, read, write, dup, dup2, fcntl, ioctl
  2. файлы и каталоги;
    stat, fstat, access, chmod, fchmod, chown, link, unlink, symlink, mkdir, opendir
  3. стандартная библиотека ввода-вывода: потоки и объекты FILE, буферизация;
    setbuf, fopen, fclose, getc, getchar, gets, putc, putchar, puts, fread, fwrte
  4. окружение процесса и запуск процессов, терминальная система, управляющий терминал, группы процессов, демоны;
    uname, gethostname, time, nice, gettimeofday, getopt, getopt_long
  5. управление процессами;
    fork, exit, wait, system, popen, pclose, exec, spawn
  6. терминальный ввод/вывод, канонический и неканонический режим, псевдотерминалы;
    stty, termcap, terminfo
  7. сигналы, ненадёжная и надёжная модель обработки, сигналы реального времени;
    signal, alarm, kill, raise, pause, sigset_t, 
    sig*set, sigprocmask, sigpending, sigaction, sigsetjmp, siglongjmp.
  8. потоки pthread_t, сигналы в потоках;
  9. расширенные операции ввода-вывода, неблокирующий ввод-вывод, асинхронные операции;
    select, poll, readv, writev
  10. межпроцессное взаимодействие: каналы, очереди сообщений, разделяемая память;
  11. синхронизации: семафоры, мьютексы, барьеры, условные переменные.

Обработка опций командной строки

Большинство стандартных утилит Linux использует единый способ для указания опций (ключей) и параметров командной строки, который обеспечивается функцией getopt(), как это показано в листинге 1. Полный код примеров можно найти в архиве hello-prog.tgz в разделе "Материалы для скачивания".

Листинг 1. Работа с командной строкой (файл mgetopt.c)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main( int argc, char *argv[] ) {
   char sopt[] = "d:t:v";
   int c, dev = 0, tim = 0, debug_level = 0;
   while( -1 != ( c = getopt( argc, argv, sopt ) ) )
      switch( c ) {
         case 'd':
            dev = atoi( optarg );
            break;
         case 't':
            tim = atoi( optarg );
            break;
         case 'v':.
            debug_level++;
            break;
         default :
            fprintf( stdout, "option must be: %s\n", sopt );
      }
   printf( "options value was:" );
   printf( "\td:%d\tt:%d\tv:%d\n", dev, tim, debug_level );
   printf( "parameters was:" );
   for( c = optind; c < argc; c++ ) printf( "\t<%s>", argv[ c ] );
   printf( "\n" );
   return 0;
};

Соберём и запустим этот пример:

$ ./mgetopt  -t 3 -d2
options value was:	d:2	t:3	v:0
parameters was:
$ ./mgetopt  -s
./mgetopt: invalid option -- 's'
option must be: d:t:v
options value was:	d:0	t:0	v:0
parameters was:
$ ./mgetopt -d 1 arg1 -t 2 arg2 -vvv
options value was:	d:1	t:2	v:3
parameters was:	<arg1>	<arg2>

Как видно, программа умеет отделять опции (ключи) от параметров, заданных в командной строке, даже если они указаны вперемешку. Стоит выработать такой стиль программирования, при котором все ваши консольные программы следовали бы подобным правилам взаимодействия с пользователем.

Системный журнал

Многие процессы в системе направляют свои сообщения не в поток стандартного вывода на терминал, а демону ведения системного журнала, который позже помещает эти сообщения в файл системного журнала /var/log/messages. Этот подход особенно актуален для программ-демонов, реализующих системные сервисы (службы), которые после запуска не получают своего экземпляра управляющего терминала.

Система ведения системного журнала Linux предусматривает целую иерархию уровней важности (тревожности) сообщений, которые определены следующими константами:

LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG

Значения, для этих констант определены в файле /usr/include/syslog.h и охватывают диапазон от 0 (критические) до 7 (отладочные).

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

К сожалению, принятая система ведения системного журнала в Linux время от времени подвергается радикальным изменениям, так, первоначально использовался демон syslogd, затем достаточно долго использовался rsyslogd, а последнее время система журналирования претерпевает изменения, связанные с идеологией загрузки systemd (инициатива дистрибутивов Fedora и RedHat).

Чаще всего в данный момент используется демон rsyslogd, и его конфигурирование осуществляется через файл /etc/rsyslog.conf. Существует ещё один способ обновить конфигурацию демона, не прекращая его работы, кроме банального перезапуска. Для этого следует послать демону сигнал SIGHUP, как показано ниже:

$ ps -Af | grep logd
root       676     1  0 21:34 ?        00:00:00 /sbin/rsyslogd -n -c 5
olej      2857  2855  0 23:00 pts/2    00:00:00 grep logd
$ sudo kill -HUP 676
$ sudo tail -n1 /var/log/messages
Aug 16 23:00:42 notebook rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" 
x-pid="676" x-info="http://www.rsyslog.com"] rsyslogd was HUPed

Перезапуск же службы системного журнала (в данном случае rsyslog) выполняется следующим образом:

$ sudo systemctl restart rsyslog.service
$ sudo tail -n4 /var/log/messages
Aug 16 16:14:50 notebook kernel: Kernel logging (proc) stopped.
Aug 16 16:14:50 notebook rsyslogd: [origin software="rsyslogd" swVersion="5.8.10"
x-pid="684" x-info="http://www.rsyslog.com"] exiting on signal 15
Aug 16 16:14:50 notebook kernel: imklog 5.8.10, log source = /proc/kmsg started.
Aug 16 16:14:50 notebook rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" 
x-pid="3533" x-info="http://www.rsyslog.com"] start

Подобными командами могут выполняться операции остановки, запуска и диагностики состояния сервиса:

$ sudo systemctl stop rsyslog.service
...
$ sudo systemctl start rsyslog.service
...
$ sudo systemctl status rsyslog.service
rsyslog.service - System Logging Service
	  Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled)
	  Active: active (running) since Thu, 16 Aug 2012 21:34:28 +0300; 1h 43min ago
	  Main PID: 676 (rsyslogd)
	  CGroup: name=systemd:/system/rsyslog.service
                 └ 676 /sbin/rsyslogd -n -c 5

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

В листинге 2 представлен пример простейшей программы, демонстрирующей запись сообщений в системный журнал и позволяющей наблюдать, как эти сообщения попадают (или не попадают) в журнал в зависимости от установок демона системного журнала (архив logd.tgz).

Листинг 2. Пример работы с системным журналом (файл mylogs.c)
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

int main( int argc, char **argv, char **envp ) {
   int i;
   openlog( argv[ 0 ], LOG_NDELAY, LOG_USER );
// LOG_EMERG=0,   LOG_ALERT=1,  LOG_CRIT=2, LOG_ERR= 3
// LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7
   for( i = LOG_EMERG; i <= LOG_DEBUG; i++ ) {
      syslog( i, "log. level = %d", i );
      printf( "done: log. level = %d\n", i );
   }
   closelog();
   return( EXIT_SUCCESS );
}

Запустим наше приложение и рассмотрим его вывод:

$ ./mylogs
done: log. level = 0
done: log. level = 1
done: log. level = 2
done: log. level = 3
done: log. level = 4
done: log. level = 5
done: log. level = 6
done: log. level = 7

Программа отправляет в системный журнал 8 сообщений, для наглядности дублируя их на терминале. Но в журнал попадают только 7 из них:

$ sudo cat /var/log/messages | tail -n7
Aug 16 18:30:12 notebook ./mylogs: log. level = 0
Aug 16 18:30:12 notebook ./mylogs: log. level = 1
Aug 16 18:30:12 notebook ./mylogs: log. level = 2
Aug 16 18:30:12 notebook ./mylogs: log. level = 3
Aug 16 18:30:12 notebook ./mylogs: log. level = 4
Aug 16 18:30:12 notebook ./mylogs: log. level = 5
Aug 16 18:30:12 notebook ./mylogs: log. Level = 6

Сообщения с уровнем LOG_DEBUG были отфильтрованы согласно текущим настройкам демона системного журнала. При других настройках демона rsyslogd порог фильтрации сообщений мог бы быть другим.

Заключение

В этой статье был представлен краткий обзор по общей архитектуре библиотек POSIX API, а также мы познакомились с аспектами, играющими большую роль при разработке программ для ОС Linux – взаимодействием с опциями и параметрами командной строки и использованием системного журнала в собственном коде.

В следующей статье мы перейдём к рассмотрению конкретных библиотек.


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=969234
ArticleTitle=Инструменты ОС Linux для разработчиков приложений для ОС Windows. Часть 13. Библиотеки API POSIX
publish-date=04222014