ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 20.06.2020

Просмотров: 412

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

КОМАНДЫ – это действия которые выполняет ‘make’. Правило может иметь более одной кманды, каждая из которых должна пиписаться с новой строки. Заметим, что вначале каждой строки команд необходимо использовать символ табуляции.

Обычно КОМАНДЫ используются в правиле с ЗАВИСИМОСТЯМИ и служат для создания файла ЦЕЛИ если изменится какая нибудь ЗАВИСИМОСТЬ. Однако есть правила, которые определяют КОМАНДЫ для ЦЕЛИ не имея ЗАВИСИМОСТЕЙ. Например, правило ,содержащее КОМАНДЫ удаления и ассоциированное с целью ‘clean’ не имеет ЗАВИСИМОСТЕЙ.

Принцип работы make следующий - после запуска, make начинает сначала просматривать содержимое файла makefile. Найдя первую ЦЕЛЬ, make смотрит по порядку написания ЗАВИСИМОСТИ. Если ЗАВИСИМОСТЬ является файлом, но не является ЦЕЛЬЮ какого нибудь другого правила, то сравниваются времена последней модификации этой ЗАВИСИМОСТИ и ЦЕЛИ. Если время последней модификации ЦЕЛИ более раннее, чем ЗАВИСИМОСТИ, то make отмечает,что необходимо выполнение КОМАНД данного правила и переходит к следующей ЗАВИСИМОСТИ. Если ЗАВИСИМОСТЬ является ЦЕЛЬЮ какого нибудь другого правила, то make вначале переходит к выполнению этого зависимого правила. Затем make возвращается к исходному правилу и смотрит времена последней модификации ЦЕЛИ и данной ЗАВИСИМОСТИ. После прохождения всех ЗАВИСИМОСТЕЙ make выполняет КОМАНДЫ. В случае, если после прохождения всех ЗАВИСИМОСТЕЙ правила выяснится, что времена последней модификации ЗАВИСИМОСТЕЙ и ЦЕЛИ совпадают, то make выполнять КОМАНДЫ не будет и прекратит работу. Замтим, что выполняется по умолчанию только одно первое встретившееся правило. Если есть правила, не являющееся ЗАВИСИМОСТЬЮ первого, то для его выполнения необходимо указать его имя в качестве аргумента make. Например ,если нам необходимо удалить ненужные файлы, и в makefile присутствует ЦЕЛЬ clean, то для ее выполнения необходимо ввести команду

make clean

При этом будет выполнена не первая ЦЕЛЬ , а та которая указана в качестве аргумента командной строки.
В примере , все файлы исходных текстов включают `defs.h', но только те которые определяют команды редактирования включают `command.h', и только файлы низкого уровня, которые изменяют буфер редактора включают `buffer.h'


edit : main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

cc -o edit main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o

main.o : main.c defs.h

cc -c main.c

kbd.o : kbd.c defs.h command.h

cc -c kbd.c

command.o : command.c defs.h command.h

cc -c command.c

display.o : display.c defs.h buffer.h

cc -c display.c

insert.o : insert.c defs.h buffer.h

cc -c insert.c

search.o : search.c defs.h buffer.h

cc -c search.c

files.o : files.c defs.h buffer.h command.h

cc -c files.c

utils.o : utils.c defs.h

cc -c utils.c

clean :

rm edit main.o kbd.o command.o display.o \

insert.o search.o files.o utils.o


Если правило не помещается на одной строке, то можно сделать перенос, пометив место переноса символом “\” (backslash).


Рассмотрим как make выполнит приведенный makefile. Выполняется первая ЦЕЛЬ. Первой ЦЕЛЬЮ является исполнимый файл редактора –edit. Эта ЦЕЛЬ зависит от объектных файлов (т.е. для получения исполнимого файла редактора необходимо собрать линковщиком следующие объектные файлы) main.o kbd.o command.o display.o insert.o search.o files.o utils.o представляющих собой ЗАВИСИМОСТИ. Далее make начинает по порядку просматривать ЗАВИСИМОСТИ. Т.к ЗАВИСИМОСТЬ main.o является ЦЕЛЬЮ правила

main.o : main.c defs.h

cc -c main.c

то make временно приостанавливает выполнение предыдущего и переходит к выполнению этого правила. main.o зависит от main.c defs.h (т.е для полученияобъектого файла main.o необходимо откомпилировать файл исходных текстов main.c в который включен заголовочный файл defs.h ). Далее make опять просматривает ЗАВИСИМОСТИ на предмет их совпадения с акой либо целью другого правила. Т.к в нашем примере совпадения нет, то сравниваются времена последней модификации файлов main.o и main.c defs.h .Если время последней модификации хотябы одного из файлов main.o и main.c более позднее, чем main.o , то make выполняет КОМАНДЫ cc -c main.c (т.е осуществляет компиляцию изменивщегося файла ЗАВИСИМОСТИ) и переходит к следующей ЗАВИСИМОСТИ предыдущей цели. При этом время последней модификации файла main.o изменится ( это время будет необходимо в предыдущей цели). Если эти времена совпадают, то КОМАНДЫ не выполняются , а происходит возврат к предыдущей цели. По приведенной аналогии make просматривает все ЗАВИСИМОСТИ цели edit. После этого сравниваютя времена последней модификации файлов ЦЕЛИ edit и ЗАВИСИМОСТЕЙ main.o kbd.o command.o display.o insert.o search.o files.o utils.o . Если время последней модификации хотябы одного из файлов ЗАВИСИМОСТИ будет более поздним чем у ЦЕЛИ , то make ваполнит КОМАНДЫ cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o для получения нового исполнимого файла редактора.

Заметим, т.к цель clean не является ЗАВИСИМОСТЬЮ ни в первой ни в подчиненных целях, то это правило простым введением команды ‘make’ никогда не выполнится. Для его выолнения необходимо, как было сказано выше ввести команду ‘make clean’. Т.к. ЦЕЛЬ clean не имеет ЗАВИСИМОСТЕЙ, то выполнится только она, и никакие другие правила выполнены не будут.


Приложение 3.


Socket-интерфейс

Данное средство было первоначально разработано для обеспечения прикладным программистам в среде ОС UNIX доступа к транспортному уровню стека протоколов TCP/IP. Позже оно было адаптировано для использования и иных протоколов (например, DECnet), а также реализовано в других операционных системах.

Socket (гнездо, разъем) - абстрактное программное понятие, используемое для обозначения в прикладной программе конечной точки канала связи с коммуникационной средой, образованной вычислительной сетью. При использовании протоколов TCP/IP можно говорить, что socket является средством подключения прикладной программы к порту (см. выше) локального узла сети.

Socket-интерфейс представляет собой просто набор системных вызовов и/или библиотечных функций языка программирования СИ, разделенных на четыре группы:

  1. локального управления;

  2. установления связи;

  3. обмена данными (ввода/вывода);

  4. закрытия связи.

Ниже рассматривается подмножество функций socket-интерфейса, достаточное для написания сетевых приложений, реализующих модель "клиент-сервер" в режиме с установлением соединения.

1.1. Функции локального управления

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

1.1.1. Создание socket'а

Создание socket'а осуществляется следующим системным вызовом

#include <sys/socket.h>

int socket (domain, type, protocol)

int domain;

int type;

int protocol;

Аргумент domain задает используемый для взаимодействия набор протоколов (вид коммуникационной области), для стека протоколов TCP/IP он должен иметь символьное значение AF_INET (определено в sys/socket.h).

Аргумент type задает режим взаимодействия:

  • SOCK_STREAM - с установлением соединения;

  • SOCK_DGRAM - без установления соединения.

Аргумент protocolзадает конкретный протокол транспортного уровня (из нескольких возможных в стеке протоколов). Если этот аргумент задан равным 0, то будет использован протокол "по умолчанию" (TCP для SOCK_STREAM и UDP для SOCK_DGRAM при использовании комплекта протоколов TCP/IP).

При удачном завершении своей работы данная функция возвращает дескриптор socket'а - целое неотрицательное число, однозначно его идентифицирующее. Дескриптор socket'а аналогичен дескриптору файла ОС UNIX.

При обнаружении ошибки в ходе своей работы функция возвращает число "-1".

1.1.2. Связывание socket'а

Для подключения socket'а к коммуникационной среде, образованной вычислительной сетью, необходимо выполнить системный вызов bind, определяющий в принятом для сети формате локальный адрес канала связи со средой. В сетях TCP/IP socket связывается с локальным портом. Системный вызов bind имеет следующий синтаксис:

#include <sys/types.h>

#include <sys/socket.h>


#include <netinet/in.h>

int bind (s, addr, addrlen)

int s;

struct sockaddr *addr;

int addrlen;

Аргумент s задает дескриптор связываемого socket'а.

Аргумент addr в общем случае должен указывать на структуру данных, содержащую локальный адрес, приписываемый socket'у. Для сетей TCP/IP такой структурой является sockaddr_in.

Аргумент addrlen задает размер (в байтах) структуры данных, указываемой аргументом addr.

Структура sockaddr_in используется несколькими системными вызовами и функциями socket-интерфейса и определена в include-файле in.h следующим образом:

struct sockaddr_in {

short sin_family;

u_short sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

Поле sin_family определяет используемый формат адреса (набор протоколов), в нашем случае (для TCP/IP) оно должно иметь значение AF_INET.

Поле sin_addr содержит адрес (номер) узла сети.

Поле sin_port содержит номер порта на узле сети.

Поле sin_zero не используется.

Определение структуры in_addr (из того же include-файла) таково:

struct in_addr {

union {

u_long S_addr;

/*

другие (не интересующие нас)

члены объединения

*/

} S_un;

#define s_addr S_un.S_addr

};

Структура sockaddr_in должна быть полностью заполнена перед выдачей системного вызова bind. При этом, если поле sin_addr.s_addr имеет значение INADDR_ANY, то системный вызов будет привязывать к socket'у номер (адрес) локального узла сети.

В случае успеха bind возвращает 0, в противном случае - "-1".

1.2. Функции установления связи

Для установления связи "клиент-сервер" используются системные вызовы listen и accept (на стороне сервера), а также connect (на стороне клиента). Для заполнения полей структуры socaddr_in, используемой в вызове connect, обычно используется библиотечная функция gethostbyname, транслирующая символическое имя узла сети в его номер (адрес).

1.2.1. Ожидание установления связи

Системный вызов listen выражает желание выдавшей его программы-сервера ожидать запросы к ней от программ-клиентов и имеет следующий вид:

#include <sys/socket.h>

int listen (s, n)

int s;

int n;

Аргумент s задает дескриптор socket'а, через который программа будет ожидать запросы к ней от клиентов. Socket должен быть предварительно создан системным вызовом socket и обеспечен адресом с помощью системного вызова bind.

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

Признаком удачного завершения системного вызова listen служит нулевой код возврата.

1.2.2. Запрос на установление соединения

Для обращения программы-клиента к серверу с запросом на установление логической соединения используется системный вызов connect, имеющий следующий вид

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

int connect (s, addr, addrlen)

int s;

struct sockaddr_in *addr;

int addrlen;

Аргумент s задает дескриптор socket'а, через который программа обращается к серверу с запросом на соединение. Socket должен быть предварительно создан системным вызовом socket и обеспечен адресом с помощью системного вызова bind.


Аргумент addr должен указывать на структуру данных, содержащую адрес, приписанный socket'у программы-сервера, к которой делается запрос на соединение. Для сетей TCP/IP такой структурой является sockaddr_in. Для формирования значений полей структуры sockaddr_in удобно использовать функцию gethostbyname.

Аргумент addrlen задает размер (в байтах) структуры данных, указываемой аргументом addr.

Для того, чтобы запрос на соединение был успешным, необходимо, по крайней мере, чтобы программа-сервер выполнила к этому моменту системный вызов listen для socket'а с указанным адресом.

При успешном выполнении запроса системный вызов connect возвращает 0, в противном случае - "-1" (устанавливая код причины неуспеха в глобальной переменной errno).

Примечание. Если к моменту выполнения connect используемый им socket не был привязан к адресу посредством bind ,то такая привязка будет выполнена автоматически.

Примечание. В режиме взаимодействия без установления соединения необходимости в выполнении системного вызова connect нет. Однако, его выполнение в таком режиме не является ошибкой - просто меняется смысл выполняемых при этом действий: устанавливается адрес "по умолчанию" для всех последующих посылок дейтаграмм.

1.2.3. Прием запроса на установление связи

Для приема запросов от программ-клиентов на установление связи в программах-серверах используется системный вызов accept, имеющий следующий вид:

#include <sys/socket.h>

#include <netinet/in.h>

int accept (s, addr, p_addrlen)

int s;

struct sockaddr_in *addr;

int *p_addrlen;

Аргумент s задает дескриптор socket'а, через который программа-сервер получила запрос на соединение (посредством системного запроса listen ).

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

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

Системный вызов accept извлекает из очереди, организованной системным вызовом listen, первый запрос на соединение и возвращает дескриптор нового (автоматически созданного) socket'а с теми же свойствами, что и socket, задаваемый аргументом s. Этот новый дескриптор необходимо использовать во всех последующих операциях обмена данными.

Кроме того после удачного завершения accept:

  1. область памяти, указываемая аргументом addr, будет содержать структуру данных (для сетей TCP/IP это sockaddr_in), описывающую адрес socket'а программы-клиента, через который она сделала свой запрос на соединение;

  2. целое число, на которое указывает аргумент p_addrlen, будет равно размеру этой структуры данных.

Если очередь запросов на момент выполнения accept пуста, то программа переходит в состояние ожидания поступления запросов от клиентов на неопределенное время (хотя такое поведение accept можно и изменить).