Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго




Скачать 17.46 Mb.
НазваниеUnix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго
страница3/143
Дата конвертации11.01.2013
Размер17.46 Mb.
ТипДокументы
1   2   3   4   5   6   7   8   9   ...   143
Глава 1. Обзор ОС UNIX

int

main(int argc, char *argv[])

{

DIR *dp;

struct dirent *dirp;

if (argc != 2)

err_quit("Использование: Is имя_каталога");

if ((dp = opendir(argv[1])) == NULL)

err_sys("невозможно открыть %s", argv[1]); while ((dirp = readdir(dp)) != NULL)

printf("%s\n", dirp->d_name);

closedir(dp); exit(O); >

Нотация ls(l) - это обычный способ указания страницы справочного руко­водства UNIX. В данном случае говорится, что страница с описанием коман­ды Is находится в первом разделе. Разделы справочного руководства обычно нумеруются цифрами от 1 до 8, а страницы в каждом разделе отсортированы по алфавиту. Здесь и далее мы будем исходить из предположения, что у вас под рукой имеется копия справочного руководства по вашей версии UNIX.

Исторически сложилось так, что все восемь разделов были объединены в документ, который называется «UNIX Programmer's Manual» (Руководство программиста UNIX). Но поскольку количество страниц в руководстве постоянно растет, появилась тенден­ция к распределению разделов по отдельным руководствам: одно для пользователей, одно для программистов и одно для системных администраторов.

В некоторых версиях UNIX разделы справочного руководства делятся на подразделы, обозначенные заглавными буквами. Так, описания всех стандартных функций ввода-вывода в AT&T [1990е] находятся в разделе 3S, например f open(3S). В некоторых сис­темах в обозначениях разделов цифры заменены алфавитными символами, например «С» - для раздела с описаниями команд.

В наши дни большинство руководств распространяется в электронном виде. Если вы располагаете такой версией справочного руководства, просмотреть справку по команде Is можно следующим образом:

man 1 Is

или

man -si Is

В предыдущем листинге представлена программа, которая просто выводит список файлов в указанном каталоге и ничего больше. Назовем файл с ис­ходным текстом myls.c и скомпилируем его в исполняемый файл с именем по умолчанию а. out:

ее myls.c

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

33

Исторически команда сс(1) запускает компилятор языка С. В системах, где использует­ся компилятор GNU С, исполняемый файл компилятора носит имя gcc(l). В этих систе­мах ее часто является ссылкой на gee.

Примерный результат работы нашей программы: $ ./a.out /dev

console

tty

mem

kmem

null

mouse

stdin

stdout

stderr

zero

и еще много строк, которые мы опустили cdrom

$ ./a.out /var/spool/cron

невозможно открыть /var/spool/cron: Permission denied $ ./a.out /dev/tty невозможно открыть /dev/tty: Not a directory

Далее в книге мы будем демонстрировать запуск программ и результаты их работы именно таким образом: символы, вводимые с клавиатуры, напечата­ны жирным моноширинным шрифтом, а результат выполнения команды - обычным мо­ноширинным шрифтом. Если нам необходимо добавить комментарий среди строк, выводимых на терминал, мы будем печатать его курсивом. Вводимым с кла­виатуры символам предшествует символ доллара; это приглашение команд­ного интерпретатора. Мы всегда будем отображать приглашение в виде сим­вола доллара.

Обратите внимание, что полученный нами список файлов не отсортирован по алфавиту. Команда Is сортирует имена файлов перед выводом.

Рассмотрим эту программу из 20 строк поближе.

В первой строке подключается наш собственный заголовочный файл spue. h. Мы будем использовать его почти во всех программах в этой кни­ге. Этот заголовочный файл в свою очередь подключает некоторые стан­дартные заголовочные файлы и определяет множество констант и прото­типов функций, которые будут встречаться в наших примерах. Листинг этого файла вы найдете в приложении В.

Функция main объявлена в соответствии со стандартом ISO С. (Более под­робно об этом стандарте рассказывается в следующей главе.)

Из командной строки мы принимаем аргумент, argv[1], который тракту-ется как имя каталога, список файлов которого нужно вывести. В главе 7 мы увидим, как вызывается функция main и каким образом программа

34

Глава 1. Обзор ОС UNIX

получает доступ к аргументам командной строки и переменным окру­жения.

  • Поскольку на практике формат записей в каталоге различен в разных системах, для получения необходимой информации мы использовали стандартные функции opendir, readdir и closedir.

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

  • Для обработки ошибочных ситуаций вызываются наши собственные функ­ции: err_sys и err_quit. В предыдущем примере мы видели, что функция err_sys выводит сообщение, описывающее возникшую ошибку («Permis­sion denied» (Доступ запрещен) или «Not a directory» (He является ката­логом)). Исходный код этих функций и их описание приводятся в прило­жении В. Кроме того, мы еще будем говорить об обработке ошибок в раз­деле 1.7.

  • По завершении программы вызывается функция exit с аргументом 0. Функция exit завершает выполнение программы. В соответствии с при­нятыми соглашениями значение 0 означает нормальное завершение про­граммы, а значения в диапазоне от 1 до 255 свидетельствуют о наличии ошибки. В разделе 8.5 мы покажем, как любая программа, в том числе и наша, может получить код завершения другой программы.

Рабочий каталог

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

Например, относительный путь к файлу doc/memo/joe означает, что файл или каталог joe находится в каталоге memo, который находится в каталоге doc, ко­торый в свою очередь должен размещаться в рабочем каталоге. Встретив та­кой путь, мы можем быть уверены, что doc и memo - это каталоги, но не можем утверждать, является ли joe каталогом или же файлом. Путь /usr/lib/lint -это абсолютный путь к файлу или каталогу lint в каталоге lib, расположен­ном в каталоге us г, который в свою очередь находится в корневом каталоге.

Домашний каталог

Когда пользователь входит в систему, рабочим каталогом становится его до­машний каталог. Домашний каталог пользователя определяется в соответ­ствии с записью в файле паролей (раздел 1.3).

5. Ввод и вывод

35

1.5- Ввод и вывод дескрипторы файлов

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

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

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

Is

Большинство командных оболочек предоставляют возможность перенаправ­ления любого из этих дескрипторов в любой файл. Например

Is > file.list

выполнит команду Is и перенаправит стандартный вывод в файл с именем

file.list.

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

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

Пример

Ниже приводится пример программы, которая читает данные со стандарт­ного ввода и копирует их на стандартный вывод. С ее помощью можно вы­полнять копирование любых обычных файлов.

Листинг 1.2. Копирование со стандартного ввода на стандартный вывод

#include "apue.h" «define BUFFSIZE 4096

int

main(void) {

int n;

char buf[BUFFSIZE];

while ((n = read(STDIN_FILENO. buf, BUFFSIZE)) > 0)

36 Глава! Обзор ОС UNIX

if (write(STDOUT_FILENO, buf, n) != n) err_sys("ошибка записи ");

if (n < 0)

err_sys("ошибка чтения");

exit(0); }

Заголовочный файл , подключаемый из файла apue. h, и константы STDIN_FILEN0 и STD0UT_FILEN0 являются частью стандарта POSIX (о котором мы будем много говорить в следующей главе). В этом файле объявлены прототи­пы функций, обеспечивающих множество сервисов, предоставляемых систе­мой UNIX, в том числе функций read и write, используемых в этом примере.

Константы STDIN_FILEN0 и STD0UT_FILEN0, определенные в файле , уста­навливают дескрипторы файлов стандартного ввода и стандартного вывода. Обычные значения этих констант - соответственно 0 и 1, однако для обеспе­чения переносимости мы будем обращаться к этим дескрипторам по именам констант.

Константу BUFFSIZE более детально мы исследуем в разделе 3.9, где увидим, как различные значения могут влиять на производительность программы. Однако независимо от значения этой константы наша программа будет в со­стоянии выполнять копирование файла.

Функция read возвращает количество прочитанных байт. Это число затем пе­редается функции write с целью указать, сколько байт нужно записать. По достижении конца файла функция read вернет значение 0 и программа завер­шит свою работу. Если в процессе чтения возникнет ошибка, read вернет зна­чение -1. Большинство системных функций в случае ошибки возвращают -1.

Если скомпилировать программу в исполняемый файл с именем по умолча­нию (а. out) и запустить ее следующим образом:

./a.out > data

то стандартный вывод будет перенаправлен в файл data, а стандартным вво­дом и стандартным выводом сообщений об ошибках будет терминал. Если выходной файл не существует, командная оболочка создаст его. Программа будет копировать строки, вводимые с клавиатуры, на стандартный вывод до тех пор, пока мы не введем символ «конец-файла» (обычно Cont rol-D).

Если мы запустим программу таким образом:

./a.out < infile > outfile

то файл с именем infile будет скопирован в файл с именем outfile.

В главе 3 мы опишем функции небуферизованного ввода-вывода более по­дробно.

1 5. Ввод и вывод

37

Стандартные функции ввода-вывода

Стандартные функции ввода-вывода предоставляют буферизованный интер­фейс к функциям небуферизованного ввода-вывода. Использование стан­дартных функций ввода-вывода избавляет нас от необходимости задумы­ваться о выборе оптимального размера буфера, например о значении кон­станты BUFFSIZE в предыдущем примере. Другое преимущество стандартных функций ввода-вывода в том, что они значительно упрощают обработку поль­зовательского ввода (что на каждом шагу встречается в прикладных про­граммах UNIX). Например, функция f gets читает из файла строку целиком, в то время как функция read считывает указанное количество байт. Как мы увидим в разделе 5.4, библиотека стандартного ввода-вывода предоставляет функции, с помощью которых можно управлять типом буферизации.

Наиболее типичным примером стандартных функций ввода-вывода являет­ся функция printf. В программах, которые обращаются к этой функции, обязательно должен быть подключен заголовочный файл (в нашем случае через подключение файла apue.h), так как он содержит прототипы всех стандартных функций ввода-вывода.

Пример

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

Листинг 1.3. Копирование со стандартного ввода на стандартный вывод с использованием стандартных функций ввода-вывода

#include "apue.h"

int

main(void)

{

int c;

while ((c = getc(stdin)) != EOF) if (putc(c, stdout) == EOF) err_sys("ошибка вывода");

if (ferror(stdin))

err_sys("ошибка ввода");

exit(O); }

фУнкция getc считывает один символ, который затем записывается с помо-Щью функции putc. Прочитав последний байт, getc вернет признак конца Файла - значение константы EOF (определена в ). Константы stdin stdout также определены в файле и обозначают стандартный ввод и стандартный вывод.

38

Глава 1. Обзор ОС UNIX

1.6. Программы и процессы Программа

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

Процессы и идентификаторы процессов

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

UNIX обеспечивает присвоение каждому процессу уникального числового идентификатора, который называется идентификатором процесса. Иденти­фикатор процесса - всегда целое неотрицательное число.

Пример

В листинге 1.4 представлена программа, которая выводит собственный иден­тификатор процесса.

Листинг 1.4. Вывод идентификатора процесса

Mnclude "apue.h"

int

main(void)

{

printf("привет от процесса с идентификатором %d\n", getpidO);

exit(O); >

Если скомпилировать эту программу в файл a. out и запустить ее, мы полу­чим примерно следующее:

$ ./a.out

привет от процесса с идентификатором 851

$ ./a.out

привет от процесса с идентификатором 854

Эта программа получает идентификатор своего процесса с помощью функ­ции getpid.

Управление процессами

Три основные функции отвечают за управление процессами: fork, exec и wa-itpid. (Функция exec имеет шесть разновидностей, но мы обычно будем ссы­латься на них просто как на функцию exec.)

1.6. Программы и процессы

39

Пример

Особенности управления процессами в UNIX мы продемонстрируем на при­мере простой программы (листинг 1.5), которая читает команды со стан­дартного ввода и выполняет их. Это похоже на примитивную реализацию командной оболочки. В программе есть несколько моментов, которые мы хо­тели бы рассмотреть поближе.

  • Для чтения строки со стандартного ввода используется функция fgets. Когда первым в строке вводится символ конца файла (обычно Control-D), fgets возвращает пустой указатель, цикл прерывается и процесс заверша­ет работу. В главе 18 мы опишем все символы, имеющие специальное на­значение - признак конца файла, забой, удаление строки и тому подоб­ное, и покажем, как можно их изменять.

  • Поскольку каждая строка, возвращаемая функцией fgets, завершается символом перевода строки, за которым следует нулевой символ, мы бу­дем определять ее длину с помощью стандартной функции strlen и заме­нять перевод строки нулевым символом. Это необходимо, потому что стро­ка, принимаемая функцией execlp в качестве аргумента, должна заканчи­ваться нулевым символом, а не символом перевода строки.

Листинг 1.5. Чтение команд со стандартного ввода и их выполнение

#include "apue.h" #include

int

main(void)

{

char buf[MAXLINE]; /* из apue.h */

pid_t pid;

int status;

printf("%% "); /* вывести приглашение (printf использует */

/* последовательность %%, чтобы вывести символ %) */ while (fgets(buf, MAXLINE, stdin) != NULL) { if (buf[strlen(buf) - 1] == '\n')

buf[strlen(buf) - 1] = 0; /* заменить символ перевода строки */

if ((pid = fork()) < 0) {

err_sys("ошибка вызова fork"); } else if (pid == 0) { /* дочерний процесс */

execlp(buf, buf, (char *)0);

err_ret("невозможно выполнить: %s", buf);

exit(127); }

/* родительский процесс */ if ((pid = waitpid(pid, &status, 0)) < 0) err_sys("ошибка вызова waitpid");

printf("%% ");

40 Глава 1. Обзор ОС UNIX

} exit(O);

}

  • Для создания нового процесса вызывается функция fork, которая создает копию вызывающего процесса. Мы называем вызывающий процесс роди­тельским процессом, а вновь созданный - дочерним. В родительском про­цессе функция fork возвращает идентификатор дочернего процесса, в до­чернем процессе - 0. Поскольку fork создает новый процесс, мы можем сказать, что она вызывается один раз - родительским процессом, а возвра­щает управление дважды - как родительскому процессу, так и дочернему.

  • Для запуска команды, прочитанной со стандартного ввода, в дочернем процессе вызывается функция execlp. Она замещает дочерний процесс но­вой программой из файла. Комбинация функций fork и exec - это своего рода двухступенчатый системный вызов, порождающий новый процесс. В UNIX эти два этапа выделены в самостоятельные функции. Более под­робно мы поговорим о них в главе 8.

  • Поскольку дочерний процесс запускает новую программу с помощью exe­clp, родительский процесс должен дождаться его завершения, прежде чем продолжить работу. Делается это с помощью вызова функции waili­pid, которой передается идентификатор дочернего процесса - аргумент pid. Функция waitpid возвращает код завершения дочернего процесса (ар­гумент status), но в нашей программе мы не используем это значение. Мы могли бы проверить его, чтобы узнать точно, как завершился дочерний процесс.

  • Одно из основных ограничений нашей программы заключается в том, что мы не можем передать аргументы выполняемой команде. Так, например, невозможно указать имя каталога, список файлов которого мы хотим по­лучить. Мы можем выполнить команду Is только для рабочего каталога. Чтобы передать аргументы, нам необходимо проанализировать введен­ную строку, в соответствии с некоторыми признаками выделить аргумен­ты (например, по символам пробела или табуляции) и затем передать их в виде отдельных аргументов функции execlp. Тем не менее наша програм­ма достаточно наглядно демонстрирует, как работают функции управле­ния процессами.

Если мы запустим программу, то получим примерно следующие результа­ты. Обратите внимание: наша программа выводит символ % в качестве при­глашения, чтобы как-то отличить его от приглашения командной оболочки.

$ ./a.out

% date

Sun Aug 1 03:04:47 EDT 2004 программисты работают допоздна

% who

sar :0 Jul 26 22:54

sar pts/0 Jul 26 22:54 (:0)

sar pts/1 Jul 26 22:54 (:0)

sar pts/2 Jul 26 22:54 (:0)

1.7. Обработка ошибок

41

% pwd

/home/sar/bk/apue/2e

% Is

Makefile

a.out

shelH.c

% AD ввод символа конца файла

$ приглашение командной оболочки

Нотация "D указывает на управляющий символ. Управляющие символы - это специ­альные символы, которые формируются при нажатой и удерживаемой клавише Cont­rol или Ctrl (в зависимости от модели клавиатуры) и одновременном нажатии на другую клавишу. Символ Control-D, или "D, представляет признак конца файла. Мы встретим еще много управляющих символов, когда будем обсуждать терминальный ввод-вывод в главе 18.

Потоки и идентификаторы потоков

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

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

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

Функции управления потоками отличны от функций управления процесса­ми. Однако, поскольку потоки были добавлены в UNIX намного позже появ­ления модели процессов, эти две модели находятся в достаточно сложной взаимосвязи, как мы увидим в главе 12.

1-7. Обработка ошибок

Очень часто при возникновении ошибки в любой из функций системы UNIX эта функция возвращает отрицательное число, а в глобальную переменную errno записывается некоторое целое, которое несет дополнительную инфор­мацию о возникшей ошибке. Например, функция open возвращает либо фай­ловый дескриптор - неотрицательное число, либо -1 в случае возникновения °Шибки. Вообще через переменную еггпо функция open может возвращать 15 Различных кодов ошибок, таких как отсутствие файла, недостаточность **Рав доступа и тому подобное. Некоторые функции следуют иному соглаше-

42

Глава 1. Обзор ОС UNIX

нию. Например, большинство функций, которые должны возвращать указа­тель на какой-либо объект, в случае ошибки возвращают пустой указатель.

Определения переменной е г гпо и констант всех возможных кодов ошибок на­ходятся в заголовочном файле <еггпо. h>. Имена констант начинаются с сим­вола Е. Кроме того, на первой странице второго раздела справочного руково­дства UNIX, которая называется intro(2), обычно перечислены все констан­ты кодов ошибок. Например, если переменная еггпо содержит код, равный значению константы EACCES, это означает, что возникли проблемы с правами доступа, например при открытии файла.

В ОС Linux коды ошибок и соответствующие им имена констант перечислены на стра­нице еггпо(З).

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

extern int еггпо;

Но в многопоточной среде адресное пространство процесса разделяется меж­ду несколькими потоками и каждый поток должен обладать своей локаль­ной копией еггпо, чтобы исключить возможность пересечения. ОС Linux, на­пример, поддерживает многопоточный доступ к переменной еггпо, определяя ее следующим образом:

extern int *__errno_location(void); #define еггпо (*__errno_location())

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

Для вывода сообщений об ошибках стандарт С предусматривает две функции.

«include

char *strerror(int errnum)',

Возвращает указатель на строку сообщения

Эта функция преобразует код ошибки errnum, обычно равный значению егг­по, в строку сообщения об ошибке и возвращает указатель на нее.

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

1.7. Обработка ошибок

43

#include

void perror(const char *msg)\

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

Пример

В листинге 1.6 приводится пример использования этих функций.

Листинг 1.6. Демонстрация функций strerror и perror

#include "apue.h" «include

int

main(int argc, char *argv[])

{

fprintf(stderr, "EACCES: %s\n", strerror(EACCES));

errno = ENOENT;

perror(argv[0]);

exit(O); }

Если эту программу скомпилировать в исполняемый файл а. out, мы получим

$ ./a.out

EACCES: Permission denied

./a.out: No such file or directory

Обратите внимание: мы передали функции perror имя исполняемого файла программы - a.out, которое находится в argv[0]. Это стандартное соглаше­ние, принятое в UNIX. Если программа выполняется в составе конвейера, как показано ниже,

progl < inputfile I prog2 | ргодЗ > outputfile

то, следуя этому соглашению, мы сможем точно определить, в какой из про­грамм произошла ошибка.

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

Восстановление после ошибок

шибки, определенные в , могут быть разделены на две категории -

J* тальные и нефатальные. Восстановление нормальной работы после фа-

адьных ошибок невозможно. Самое лучшее, что мы можем сделать, - это

44

1   2   3   4   5   6   7   8   9   ...   143

Похожие:

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconСтивене Д. 80 Сознавание: исследуем, экспериментируем, упражняемся/ Пер с англ. А. Пилюгина
Разработка серийного оформления художника В. Щербакова Серия основана в 1999 году

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconРичард Д. Деловые культуры в международном бизнесе. От столкновения к взаимопониманию / Льюис Ричард Д.; пер с англ. Т. А. Нестика
Европа: вчера, сегодня, завтра / ран, Ин-т Европы; отв ред., авт предисл. Н. П. Шмелев. – М.: Экономика, 2002. – 823с

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconРичард Докинз. Эгоистичный ген
Ричард Докинз профессор Оксфордского университета, автор таких известных книг, как "Эгоистический ген", "Слепой часовщик", "Расширенный...

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconСтивен Кинг После заката Стивен Кинг После заката Посвящается Хайди Питлор
Он, бесформенный, присвоил чужую форму. Как такое могло случиться, Остин? Нет, как такое может быть? И почему тогда солнце не померкнет,...

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconРичард Флорида, «Креативный класс: люди, которые меняют будущее». М
Ричард Флорида, «Креативный класс: люди, которые меняют будущее». М.: Классика-xxi, 2005. (Richard Florida, “The Rise of The Creative...

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconРичард Фарсон Менеджмент абсурда. Парадоксы лидерства Публикуется по: © "София", 2001 Перевод с англ. © А. Левицкий Об
Роджерсом (одним из этих проектов был получивший академическую премию документальный фильм "Путешествие в себя"). Ричард Фарсон получил...

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconThe Roles of Intermediaries in Cluster Development: The Thai Experiences from High-Tech and mid-tech manufacturing, knowledge-intensive services, to

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconHigh School/High Tech Program Guide

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconКультура Социогуманитарные исследования Издание второе, дополненное
Борисов С. Б. Человек. Текст. Культура. Социогуманитарные исследования. Издание второе, дополненное. – Шадринск, 2007 – 556 с

Unix профессиональное программирование Второе издание У. Ричард Стивене, Стивен а раго 2007 Серия «High tech» У. Ричард Стивене, Стивен А. Раго iconЕстественно-математические науки. Техника Барр, С. Россыпи головоломок [Текст] / Стивен Барр; пер с англ. Ю. Н. Сударева. М. Мир, 1987. 415 с
Барр, С. Россыпи головоломок [Текст] / Стивен Барр; пер с англ. Ю. Н. Сударева. М. Мир, 1987. 415 с


Разместите кнопку на своём сайте:
lib.convdocs.org


База данных защищена авторским правом ©lib.convdocs.org 2012
обратиться к администрации
lib.convdocs.org
Главная страница