Смекни!
smekni.com

Методические указания по выполнению курсовых работ по дисциплине «Системное программное обеспечение» для студентов, обучающихся по специальностям 210100, 210200 и направлению 550200 (стр. 5 из 9)

// плавающей точкой удвоенной точности //

char * fuffer ; // объявляется указатель с именем fuffer,

// который указывает на переменную типа char

double nomer;

void *addres;

addres = & nomer;

(double *)addres ++;

Переменная addres объявлена как указатель на объект любого типа. Поэтому ей можно присвоить адрес любого объекта (& - операция вычисления адреса). Однако, как было отмечено выше, ни одна арифметическая операция не может быть выполнена над указателем, пока не будет явно определен тип данных, на которые он указывает. Это можно сделать, используя операцию приведения типа (double *) для преобразования addres к указателю на тип double, а затем увеличение адреса.

const * dr;

Переменная dr объявлена как указатель на константное выражение, т.е. значение указателя может изменяться в процессе выполнения программы, а величина, на которую он указывает, нет.

unsigned char * const w = &obj.

Переменная w объявлена как константный указатель на данные типа char unsigned. Это означает, что на протяжении всей программы w будет указывать на одну и ту же область памяти. Содержание же этой области может быть изменено.

Указатель на величину одного типа может быть преобразован к указателю на величину другого типа. Однако результат может быть не определен из-за отличий в требованиях к выравниванию и размерах для различных типов.

Указатель на тип void может быть преобразован к указателю на любой тип, и указатель на любой тип может быть преобразован к указателю на тип void без ограничений. Значение указателя может быть преобразовано к целой величине. Метод преобразования зависит от размера указателя и размера целого типа следующим образом:

- если размер указателя меньше размера целого типа или равен ему, то указатель преобразуется точно так же, как целое без знака;

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

Целый тип может быть преобразован к адресному типу по следующим правилам:

- если целый тип того же размера, что и указатель, то целая величина просто рассматривается как указатель (целое без знака);

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

Массивы и указатели на массивы

С++ и С интерпретируют имя массива как адрес его первого элемента. Таким образом, если x – массив, то выражения &x[0] и x эквивалентны. В случае матрицы – назовем ее mat – выражения &mat[0][0] и mat также эквивалентны.

С++ позволяет использовать указатели для обращения к разным элементам массива. Когда Вы вызываете элемент x[i] массива x , то выполняются две задачи: получит базовый адрес массива, т.е. узнать где находится первый элемент массива и использовать i для вычисления смещения базового адреса массива. Фактически, если есть указатель ptr на базовый адрес массива, т.е.

prt = x;

то можно записать :

адрес элемента x[i] = адрес x + i * sizeof(базовый тип)

или

адрес элемента x[i] = ptr + i * sizeof(базовый тип)

В С++ это записывается более коротким образом

адрес элемента x[i] = ptr + i

При последовательном обращении к элементам массива можно использован метод инкремента/декремента указателя, т.е. операцию ++ и --, например

ptr++

a = * ptr++

a = * (ptr--)

Указатели на структуры

С++ поддерживает объявление и использование указателей на структуры. Для присвоения адреса структурной переменной указателю того же типа используется тот же синтаксис, что и в случае простых переменных. После того, как указатель получит адрес структурной переменной, для обращения к элементам структуры нужно использовать операцию ->.

Общий синтаксис для доступа к элементам структуры с помощью указателя имеет вид

structPtr -> aMember

где structPtr –указатель на структуру, aMember – элемент структуры.

Пример:

struct point

{

double x;

double y;

};

point p;

point* ptr = & p;

ptr->x = 23.3;

ptr->y = ptr->x + 12.3;

Динамическое распределение ОП

4.3 Операции new и delete

Существует много приложений, в которых необходимо создавать новые переменные и динамически распределять для них память во время выполнения программы. В языке С для этого имеются функции динамической памяти, такие как malloc, calloc, free. В С++ введены новые операции - new и delete, которые лучше контролируют тип создаваемых динамических данных. К тому же эти операции работают с конструкторами и деструкторами ( раздел объектно-ориентированное программирование).

Операции new и delete имеют следующий синтаксис:

указатель = new тип;

delete указатель;

Операция new возвращает адрес динамически распределенной переменной. Операция delete освобождает динамически распределенную память, на которую ссылается указатель.

Если динамическое распределение памяти с помощью операции new потерпело неудачу, оно выбрасывает исключение типа xalloc, объявленное в заголовочном файле EXCEPT.H, поэтому часто динамическое распределение памяти осуществляется внутри блока try. Пример:

try

{

int *pint;

pint = new int;

*pint = 33;

cout << “Указателю выделена память и в ней хранятся данные” << *pint << endl;

delete pint;

}

catch (xalloc&)

{

cout << “Ошибка при выделении памяти” << endl;

Сигналы

Сигналы — это программные прерывания, предоставляющие механизм для обработки асинхронных событий. Такие события могут происходить из-за пределов системы — например, из-за введения пользователем символа прерываний (обычно Ctrl+C) — или возникать вследствие действий в программе или ядре, например, когда процесс исполняет код, в котором выполняется деление на ноль. В виде примитивной формы взаимодействия между процессами (interprocess communication, IPC) один_процесс также может отправлять сигналы другому процессу.

Самое главное в сигналах — это не только то, что события происходят асинхронно, например пользователь может нажать Ctrl+C в любой момент выполнения программы, но и то, что программа асинхронно обрабатывает сигналы. Функции обработки сигналов регистрируются в ядре, которое асинхронно вызывает функции из оставшейся части программы, как только сигналы доставляются.

Сигналы существуют в Unix с самого рождения системы. Со временем, однако, они эволюционировали, наиболее значительно — в терминах надежности, поскольку когда-то сигналы могли теряться, и в терминах функциональности, поскольку теперь сигналы могут переносить данные, определяемые пользователем. Вначале в разных системах Unix вносились несовместимые друг с другом изменения в сигналы. К счастью, на помощь пришел стандарт POSIX, в котором обработка сигналов была стандартизирована. Именно этот стандарт обеспечивается в Linux и именно о нем мы будем говорить далее.

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

4.4 Демоны

Важную роль в работе операционной системы .играют системные демоны. Демоны — это неинтерактивные процессы, которые запускаются обычным образом - путем загрузки в память соответствующих им программ (исполняемых файлов) и выполняются в фоновом режиме. Обычно демоны запускаются при инициализации системы и обеспечивают работу различных подсистем UNIX; системы терминального доступа, системы печати, системы - сетевого доступа и сетевых услуг и т. д. Демоны не связаны ни с одним пользовательским сеансом работы и не могут непосредственно управляться пользователем. Большую часть времени демоны ожидают пока тот или иной процесс запросит определенную услугу, например, доступ к файловому архиву или печать документа. Возможность терминального входа пользователей в систему, доступ по сети, использование системы печати и электронной почты, — все это обеспечивается соответствующими демонами. Некоторые демоны работают постоянно, пример такого демона — процесс init(), являющийся прародителем всех прикладных процессов в системе. Другими примерами являются сrоn(), позволяющий запускать программы в определенные моменты, времени, inetd(), обеспечивающий доступ к сервисам системы из сети, и sendmail(), обеспечивающий получение и отправку электронной почты.

На рисунке 2 приведен основной алгоритм неинтерактивной программы. В ней вызываются функции, необходимые для выполнения программы, отслеживающей запущенные процессы. Данная функция организовывает собственную группу и сеанс, не имеющие управляющего терминала. Так как лидером группы и сеанса может стать процесс, если он еще не является лидером, а предыстория запуска данной программы неизвестна, необходима гарантия, что процесс не является лидером. Для этого порождает дочерний процесс. Так как его PID уникален, то ни группы ни сеансы с таким идентификатором не существует, а значит, нет и лидера. При этом родительский процесс немедленно завершает выполнение, поскольку он уже не нужен. Существует еще. одна причина необходимости порождения дочернего процесса. Если демон был запущен из командной строки командного интерпретатора shell не в фоновом режиме, последний будет ожидать завершения выполнения демона, и таким образом, терминал будет заблокирован. Порождая процесс и завершая выполнение родителя, имитируется для командного интерпретатора завершение работы демона, после чего shell выведет свое приглашение. Далее закрываются открытые файлы, и выполняется запись в системном журнале. Для этого сначала устанавливаются опции "ведения журнала" каждая запись будет предваряться