Вы на НеОфициальном сайте факультета ЭиП

На нашем портале ежедневно выкладываются материалы способные помочь студентам. Курсовые, шпаргалки, ответы и еще куча всего что может понадобиться в учебе!
Главная Контакты Карта сайта
 
Где мы?
» » » Системные функции Unix. Лабораторная работа №6.1

Реклама


Системные функции Unix. Лабораторная работа №6.1

Просмотров: 2561 Автор: admin
Системные функции Unix. Лабораторная работа №6.1 
Методические указания
Функция gettimeofday():Системные часы .

Функция gettimeofday() определяет текущее системное время. В качестве аргумента она
принимает структуру типа timeval, в которую записывается значение времени (в секундах ),
прошедшее с начала эпохи Unix (1 января 1970 года , полночь по Гринвичу). Это значение
делится на два поля. В поле tv_sec хранится целое число секунд, а в поле tv_usec
дополнительное число микросекунд. У функции есть также второй аргумент, который
должен быть равен NULL. Функция объявлена в файле <sys/time.h>.
Результат, возвращаемый функцией gettimeofday(), мало подходит для отображения на
экране, поэтому существуют библиотечные функции localtime() и strftime(), преобразующие
это значение в нужный формат. Функция localtime() принимает указатель на число секунд
(поле tv_sec структуры timeval) и возвращает указатель на структуру типа tm. Эта структура
содержит поля , заполняемые параметрами времени в соответствии с локальным часовым
поясом:
– tm_hour, tm_min, tm_sec текущее время (часы ,минуты ,секунды );
– tm_year, tm_mon, tm_day год ,месяц ,день ;
– tm_wday день недели (значение 0соответсвует воскресенью );
– tm_yaday день года ;
– tm_isdst флаг ,указывающий ,учтено ли летнее время .

Функция strftime() на основании структуры tm создаст строку, отформатированную по
заданному правилу . Формат напоминает тот, что используется в функции printf(): указывается
строка с кодами, определяющими включаемые поля структуры. Например , форматная строка
вида
%Y-%m-%d %H:%M:%S
соответствует такому результату:
2007-11-20 11:03:05
Функции strftime() необходимо задать указатель на текстовый буфер, куда будет
помещена полученная строка, длину буфера, строку формата и указатель на структуру типа
tm. Следует учесть, что ни функция localtime(), ни функция strftime() не учитывают дробную
часть текущего времени (поле tv_usec структуры timeval). Об этом должен позаботится
программист.
Объявления функций localtime() и strftime() находятся в файле <time.h>
Программа приведенная в листинге №1 отображает текущие дату и время с точностью до
миллисекунды.
Листинг № 1
/*print-time.c -- Отображение даты и времени.*/
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

void print_time ()
{
struct timeval tv;
struct tm* ptm;
char time_string[40];
long milliseconds;


/* Определение текущего времени и преобразование
полученного значения*/

gettimeofday (&tv, NULL);
ptm = localtime (&tv.tv_sec);

/* Форматирование значения даты и времени с точностью до
секунды.*/

strftime (time_string, sizeof (time_string), "%Y-%m-%d %H:
%M:%S", ptm);

/* Вычисление количества миллисекунд. */

milliseconds = tv.tv_usec / 1000;

/* Отображение времени. */

printf ("%s.%03ld\n", time_string, milliseconds);
}

int main ()
{
print_time();
return 0;
}

Семейство функций mlock():
блокирование физической памяти.

Функции семейства mlock() позволяют программе блокировать часть своего адресного
пространства в физической памяти. Заблокированные страницы не будут выгружены
операционной системой в раздел подкачки, даже если программа долго к ним не обращалась.
Блокирование физической памяти важно в программах реального времени, поскольку
задержки, связанные с выгрузкой и подкачкой страниц, могу оказаться слишком длинными
лии возникать в самый неподходящий момент. Приложения ,заботящиеся о безопасности
своих данных , могут устанавливать запрет на выгрузку важных данных в файл подкачки, в
котором они станут доступны злоумышленнику после завершения программы.
Чтобы заблокировать область памяти, достаточно вызвать функцию mlock(), передав
ей указатель на начало области и значение длины области. ОС Linux разбивает память на
страницы и соответственно блокирует ее постранично: любая страница, которую захватывает
(хотя бы частично) заданная в функции mlock() область памяти, окажется заблокированной.
Определить размер системной страницы позволяет функция getpagesize(). В Linux-системах,
работающих на платформе x86, эта величина составляет 4Кбайт.
Вот как можно выделить и заблокировать 32Мбайт оперативной памяти:
const int alloc_size = 32 * 1024 * 1024;
char* memory = malloc (alloc_size);
mlock (memory, alloc_size);
Выделение страницы и блокирование ее с помощью функции mlock() еще не означает, что
эта страница будет предоставлена данному процессу, поскольку выделение памяти может
происходит в режиме копирования при записи (режим копирования при записи означает, что
Linux создает для процесса частную копию страницы только тогда, когда процесс записывает 
в нее какие-то данные ). Следовательно , каждую страницу необходимо проинициализировать:
size_t i;
size_t page_size = getpagesize();
for (i=0; i < alloc_size; i += page_size)
memory[i] = 0;
Процессу, осуществляющему запись на страницу, операционная система предоставит в
монопольное использование ее уникальную копию.
Для разблокирования области памяти следует вызвать функцию munlock(), передав ей
те же аргументы, что и функции mlock().
Функция mlockall() блокирует все адресное пространство программы и принимает
единственный флаговый аргумент. Флаг MCL_CURRENT | MLC_FUTUTRE позволяет
блокировать все память программы, как текущую, так и будущую.
Блокирование больших объемов памяти, особенно с помощью функции mlockall(),
несет потенциальную угрозу всей системе. Несправедливое распределение оперативной
памяти приведет к катастрофическому снижению производительности системы, так как
остальным процессам придется сражаться друг с другом за небольшой «клочок» памяти , в
следствии чего они будут постоянно выгружаться на диск и загружаться обратно. Может
даже возникнуть ситуация, когда оперативная память закончится и система начнет
уничтожать процессы . По этой причине функции mlock() и mlockall() доступны только
суперпользователю. Если какой-нибудь другой пользователь попытается вызвать одну из этих
функций, она вернет значение -1, а в переменную errno будет записан код EPERM.
Функция munlockall() разблокирует всю память текущего процесса.
Контролировать использование памяти удобнее всего с помощью команды top. В
колонке SIZE ее выходных данные показывается размер виртуального адресного
пространства каждой программы (общий размер сегментов кода, данных и стека с учетом
выгруженных страниц ). В колонке RSS приводится объем резидентной части программы.
Сумма значений в столбце RSS не может превышать объем ОЗУ, а суммарный показатель по
столбцу SIZE не может быть больше 2Гбайт (в 32-разрядных версиях Linux).
Функции семейства mlock() объявлены в файле <sys/mman.h>

Функция mprotect(): задание прав доступа к памяти.

В одной из предыдущих лабораторных работ рассказывалось о том, как осуществляется
отображение файла в памяти. Вспомните , что третьим аргументом функции mmap() является
битовое объединение флагов доступа: флаги PROT_READ, PROT_WRITE и PROT_EXEC
задают права чтения, записи и выполнения файла, а флаг PROT_NONE означает запрет
доступа. Если программа пытается выполнить над отображаемым файлом недопустимую
операцию, ей посылается сигнал SIGSEGV (нарушение сегментации ), который приводит к
завершению программы .
После того как файл был отображен в памяти, изменить права доступа к нему
позволяет функция mprotect(). Ее аргументами является адрес области памяти, размер
области и новый набор флагов доступа. Область должна состоять из целых страниц, т .е.
начинаться и заканчиваться на границе между страницами. Учтите , что память, выделяемая
функцией malloc(), обычно не выравнивается по границе страниц, даже если размер области
кратен размеру страницы. Если требуется защищать память, выделяемую функцией malloc(),
нужно запросить более крупный блок, а затем найти в нем участок, выравненный по границе
страниц.
Кроме того , с помощью функции mmap() можно обойти функцию malloc() и
запрашивать память непосредственно у ядра Linux.
Предположим, к примеру, что программы выделяет страницу, отображая в памяти
файл /dev/zero. Память инициализируется как для чтения, так и для записи:
int fd = open ('/dev/zero/', O_RDONLY);
char* memory = mmap (NULL, page_size, PROT_READ | PROT_WRITE,  
MAP_PRIVATE, fd, 0);
close (fd);
Далее программа запрещает запись в эту область памяти, вызывая функцию mprotect():
mprotect (memory, page_size, PROT_READ);
Существует оригинальная методика контроля памяти: можно защитить область
памяти с помощью функций mmap() и mprotect(), а затем обработать сигнал SIGSEGV,
посылаемый при попытке доступа к этой памяти. Эта методика иллюстрируется в
следующем листинге :
Листинг № 2
/* mprotect.c -- Обнаружение попыток доступа к памяти благодаря
функции mprotect() */

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

static int alloc_size;
static char* memory;

void segv_handler (int signal_number)
{
printf ("memory accessed! \n");
mprotect (memory, alloc_size, PROT_READ | PROT_WRITE);
}

int main()
{
int fd;
struct sigaction sa;

/* Назначение функции segv_handler() обработчиком сигнала
SIGSEVG. */

memset (&sa, 0, sizeof (sa));
sa.sa_handler = &segv_handler;
sigaction (SIGSEGV, &sa, NULL);

/* Выделение одной страницы путем отображения в памяти
файла /dev/zero. Сначала память доступна только для записи. */
alloc_size = getpagesize ();
fd = open ("/dev/zero", O_RDONLY);
memory = mmap (NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
close (fd);

/* Запись на страницу для получения ее копии в частное
использование. */

memory[0] = 0;

/* Запрет на запись в память. */

mprotect (memory, alloc_size, PROT_NONE);

/* Попытка записать в память. */

memory[0] = 1;

/* Удаление памяти. */

printf ("all done\n");
munmap (memory, alloc_size);
return 0;
}

Программа работает по следующей схеме.
1. Задается обработчик сигнала SIGSEGV.
2. Файл /dev/zero отображается в памяти, из которой выделяется одна страница.
В эту страницу записывается инициализирующее значение, благодаря чему программе
предоставляется частная копия страницы.
3. Программа защищает память, вызывая функцию mprotect() с флагом PROT_NONE.
4. Когда программа впоследствии обращается к памяти, Linux посылает ей сигнал
SIGSEGV, который обрабатывается в функции segv_handler(). Обработчик сигнала
отменяет защиту памяти, разрешая выполнить операцию записи.
5. Программа удаляет область памяти с помощью функции munmap().

Функция nanosleep(): высокоточная пауза.

Функция nanosleep() является более точной версией стандартной функции sleep(), принимая
указатель на структуру типа timespec, где время задается с точностью до наносекунды, а не
секунды. Правда особенности работы ОС Linux таковы , что реальная точность оказывается
равной 10мс, но это все равно выше чем в функции sleep(). Функцию nanosleep() можно
использовать в приложениях, где требуется запускать различные операции с короткими
интервалами между ними.
В структуре timespec имеется два поля:

– tv_sec целое число секунд ;
– tv_nsec дополнительное число миллисекунд (должно быть меньше ,чем миллиард ).

Работа функции nanosleep(), как и функции sleep(), прерывается при получении
сигнала. При этом функция возвращает значение -1, а в переменную errno записывается код
EINTR. Но у функции nanosleep() есть важное преимущество. Она принимает
дополнительный аргумент еще один указатель на структуру timespec,в которую (если
указатель не равен NULL) заносится величина оставшегося интервала времени (т.е. разница
между запрашиваемым и прошедшим промежутками времени). Благодаря этому можно легко
возобновлять прерванные операции ожидания. В ниже приведённом листинге показана
альтернативная реализация функции sleep(). В отличие от стандартного системного вызова
эта функция может принимать дробное число секунд и возобновлять операцию в случае
прерывания по сигналу.
Листинг № 3.


/* better-sleep.c -- Высокоточная реализация функции sleep(). */

#include <errno.h>
#include <time.h>

int better_sleep (double sleep_time)
{
struct timespec tv;
/* Заполнение структуры timespec на основании указанного
числа секунд. */
tv.tv_sec = (time_t) sleep_time;
/* Добавление неучтенных наносекунд. */
tv.tv_nsec = (long) ((sleep_time - tv.tv_sec) *1e+9);
while (1)
{
/* Пауза, длительность которой указана в переменной tv.
* В случае прерывания по сигналу величина оставшегося
* промежутка времени заносится обратно в переменую tv.
*/

int rval = nanosleep (&tv, &tv);
if (rval == 0)
/* Пауза успешно окончена. */
return 0;
else if (errno == EINTR)
/* Прервывание по сигналу. Повторная попытка. */
continue;
else
/* Какая-то другая ошибка. */
return rval;
}
return 0;
}

int main()
{
double sleep_time = 5.2393;
better_sleep (sleep_time);
return 0;
}

Функция sendfile(): быстрая передача данных.

Функция sendfile() - это э ективный механизм копирования данных из одного файлового
дескриптора в другой. Дескрипторам могут соответствовать дисковые файлы, сокеты или
устройства.
Обычно цикл копирования реализуется следующим образом. Программа выделяет
буфер фиксированного размера, перемещает в него данные из исходного дескриптора, затем
записывает содержимое буфера во второй дескриптор и повторяет описанную процедуру до
тех пор , пока не будут скопированы все данные. Такая схема неэ ективна как с точки
зрения времени , так и с точки зрения затрат памяти, поскольку выделяется дополнительный
буфер и над его содержимым выполняются операции копирования.


Функция sendfile() устраняет потребность в создании промежуточного буфера. Ей
передаются дескриптор для записи, дескриптор для чтения, указатель на переменную
смещения и число копируемых данных. Переменная смещения определяет позицию входного
файла, с которой начинаться копирование (0 это начало файла ).После окончания
копирования переменная будет содержать смещение конца блока. Функция sendfile()
объявлена в файле <sys/sendfile.h>.
Программа, показанная в нижеприведенном листинге, предоставляет собой простую,
но очень э ективную реализацию механизма файлового копирования. Она принимает в
командной строке два имени файла и копирует содержимое первого файла во второй. Размер
исходного файла определяется с помощью функции fstat().
Листинг № 4.
/* copy.c -- Копирование файла с помощью функции sendfile() */

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main (int argc, char* argv[])
{
int read_fd;
int write_fd;
struct stat stat_buf;
off_t offset = 0;

/* Открытие входного файла. */

read_fd = open (argv[1], O_RDONLY);

/* Определение размера входного файла. */

fstat (read_fd, & stat_buf);

/* Открытие выходного файла для записи. */

write_fd = open (argv[2], O_WRONLY | O_CREAT,
stat_buf.st_mode);

/* Передача данных из одного файла в другой. */

sendfile (write_fd, read_fd, &offset, stat_buf.st_size);

/* Закрытие файлов. */

close (read_fd);
close (write_fd);

return 0;
}

Функция setitimer(): задание интервальных таймеров.

Функция setitimer() является обобщением системного вызова alarm(). Она планирует
доставку сигнала по истечении заданного промежутка времени.
С помощью функции setitimer() можно создавать таймеры трех типов.
– ITIMER_REAL. По истечении указанного времени процессу посылается сигнал
SIGALRM.
– ITITIMER_VIRTUAL. После того как процесс отработал требуемое время, ему
посылается сигнал SIGVTALRM. Время , когда процесс не выполнялся (работало ядро
или другой процесс), не учитывается.
– ITIMER_PROF. По истечении указанного времени процессу посылается сигнал SIGPROF.
Учитывается время выполнения самого процесса, а также запускаемых в нем системных
вызовов.

Код таймера задается в первом аргументе функции setitimer(). Второй аргумент это
указатель на структуру типа itimerval, содержащую параметры таймера. Третий аргумент
либо равен NULL, либо является указателем на другую структуру itimerval, куда будут
записаны прежние параметры таймера.
В структуре itimerval два поля.
– it_value. Здесь находится структура типа timeval, где записано время отправки сигнала.
Если это поле равно нулю, таймер отменяется.
– it_interval. Это еще одна структура timeval, определяющая , что произойдет после
отправки первого сигнала. Если она равна нулю, таймер будет отменен. В противном
случае здесь записан интервал генерирования сигналов.

В следующем листинге показано, как с помощью функции setitimer() отслеживать
выполнение программы . Таймер настроен на интервал 250 мс , по истечении которого
генерируется сигнал SIGVTALRM.
Листинг № 5
/* timer.c -- Пример создания таймера. */

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

void timer_handler (int signum)
{
static int count = 0;
printf ("timer expired %d times\n", ++count);
}

int main()
{
struct sigaction sa;
struct itimerval timer;

/* Назначение функции timer_handler обработчиком сигнала
SIGVTALRM. */

memset (&sa, 0, sizeof (sa));


sa.sa_handler = &timer_handler;
sigaction (SIGVTALRM, &sa, NULL);

/* Таймер сработает через 250 миллисекунд... */

timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 250000;

/* ... и будет продолжать активизироваться каждые 250
миллисекунд. */

timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 250000;

/* Запуск виртуального таймера. Он подсчитывает фактической
время работы процесса. */

setitimer (ITIMER_VIRTUAL, &timer, NULL);

while(1);
}

Функция sysinfo(): получение системной статистики.

Функция sysinfo() возвращает системную статистику. Ее единственным аргуменом является
указатель на структуру типа sysinfo. Перечислим наиболее интересные поля этой структуры.
– uptime время в секундах ,прошедшее с момента загрузки системы ;
– totalram общий объем оперативной памяти ;
– freeram свободный объем ОЗУ ;
– procs число процессов ,работающих в системе .

Для использования функции sysinfo() требуется включить в программу файлы
<linux/kernel.h> и <sys/sysinfo.h>.
Программа, приведенная в следующем листинге отображает статистическую
информацию о текущем состоянии системы.
Листинг № 6
/* sysinfo.c -- Вывод системной статистики. */

#include <linux/kernel.h>
#include <stdio.h>
#include <sys/sysinfo.h>

int main ()
{
/* Константы преобразования. */

const long minute = 60;
const long hour = minute*60;
const long day = hour * 24;
const double megabyte = 1024 * 1024;

/* Получение системной статистики. */

struct sysinfo si;
sysinfo (&si);

/* Предоставление информации в понятном виде. */

printf ("system uptime : %ld days, %ld:%02ld:%02ld\n",
si.uptime / day, (si.uptime % day) / hour,
(si.uptime/ hour)/ minute, si.uptime % minute);

printf ("total RAM : %5.1f Mb\n", si.totalram /
megabyte);

printf ("free RAM : %5.1f Mb\n", si.freeram / megabyte);

printf ("process count : %d\n", si.procs);
return 0;
}

Функция uname()

Функция uname() возвращает информацию о системе, в частности сетевое и доменное
имена компьютера , а также версию операционной системы. Единственным аргументом
функции является указатель на структуру типа utsname. Функция заполняет следующие поля
этой структуры (все поля содержат текстовые строки).

– sysname. Здесь содержится имя операционной системы.
– release, version. В этих полях указывается номера версии и модификации ядра.
– machine. Здесь приводится информация о платформе, на которой работает платформа.
– node. Это имя компьютера.
– __domain. Имя домена.

Функция uname() объявлена в файле <sys/utsname.h>
Листинг № 7
#include <stdio.h>
#include <sys/utsname.h>

int main ()
{
struct utsname u;
uname (&u);
printf ("%s release %s (version %s) on %s\n", u.sysname,
u.release, u.version, u.machine);
return 0;
}

Порядок проведения работы.
1. Прочитать методический материал.
 2. Скомпилировать программы.



Информация

Комментировать статьи на нашем сайте возможно только в течении 60 дней со дня публикации.

Популярные новости

Статистика сайта



Rambler's Top100



 
Copyright © НеОфициальный сайт факультета ЭиП