Программирование для Linux. Профессиональный подход - Марк Митчелл
Шрифт:
Интервал:
Закладка:
С помощью функции setitimer() можно создавать таймеры трех типов.
■ ITIMER_REAL. По истечении указанного времени процессу посылается сигнал SIGALRM.
■ ITIMER_VIRTUAL. После того как процесс отработал требуемое время, ему посылается сигнал SIGVTALRM. Время, когда процесс не выполнялся (работало ядро или другой процесс), не учитывается.
■ ITIMER_PROF. По истечении указанного времени процессу посылается сигнал SIGPROF. Учитывается время выполнения самого процесса, а также запускаемых в нем системных вызовов.
Код таймера задается в первом аргументе функции setitimer(). Второй аргумент — это указатель на структуру типа itimerval, содержащую параметры таймера. Третий аргумент либо равен NULL, либо является указателем на другую структуру itimerval, куда будут записаны прежние параметры таймера.
В структуре itimerval два поля.
■ it_value. Здесь находится структура типа timeval, где записано время отправки сигнала. Если это поле равно нулю, таймер отменяется.
■ it_interval. Это еще одна структура timeval, определяющая, что произойдет после отправки первого сигнала. Если она равна нулю, таймер будет отменен. В противном случае здесь записан интервал генерирования сигналов.
Структура timeval была описана в разделе 8.7. "Функция gettimeofday(): системные часы"
В листинге 8.11 показано, как с помощью функции setitimer() отслеживать выполнение программы. Таймер настроен на интервал 250 мс, по истечении которого генерируется сигнал SIGVTALRM.
Листинг 8.11. (itimer.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 timesn", ++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);
}
8.14. Функция sysinfo(): получение системной статистики
Функция sysinfo() возвращает системную статистике. Ее единственным аргументом является указатель на структуру типа sysinfo. Перечислим наиболее интересные поля этой структуры.
■ uptime — время в секундах, прошедшее с момента загрузки системы;
■ totalram — общий объем оперативной памяти;
■ freeram — свободный объем ОЗУ;
■ procs — число процессов, работающих в системе.
Для использования функции sysinfo() требуется включить в программу файлы <linux/kernel.h>, <linux/sys.h> и <sys/sysinfo.h>.
Программа, приведенная в листинге 8.12, отображает статистическую информацию о текущем состоянии системы.
Листинг 8.12. (sysinfo.c) Вывод системной статистики#include <linux/kernel.h>
#include <linux/sys.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:%021dn",
si.uptime / day, (si.uptime % day) / hour,
(si.uptime % hour) / minute, si.uptime % minute);
printf("total RAM : %5.1f MBn", si.totalram / megabyte);
printf("free RAM : %5.1f MBn",
si.freeram / megabyte);
printf("process count : %dn", si.procs);
return 0;
}
8.15. Функция uname()
Функция uname() возвращает информацию о системе, в частности сетевое и доменное имена компьютера, а также версию операционной системы. Единственным аргументом функции является указатель на структуру типа utsname. Функция заполняет следующие поля этой структуры (все эти поля содержат текстовые строки).
■ sysname. Здесь содержится имя операционной системы (например, Linux).
■ release, version. В этих полях указываются номера версии и модификации ядра.
■ machine. Здесь приводится информация о платформе, на которой работает система. В случае Intel-совместимых компьютеров это будет либо i386, либо i686, в зависимости от процессора.
■ node. Это имя компьютера.
■ __domain. Это имя домена.
Функция uname() объявлена в файле <sys/utsname.h>.
В листинге 8.13 показана небольшая программа, которая отображает номера версии и модификации ядра Linux, а также сообщает тип платформы.
Листинг 8.15. (print-uname.c) Вывод информации о ядре и платформе#include <stdio.h>
#include <sys/utsname.h>
int main() {
struct utsname u;
uname(&u);
printf("%s release %s (version %s) on %sn", u.sysname,
u.release, u.version, u.machine);
return 0;
}
Глава 9
Встроенный ассемблерный код
Сегодня лишь немногие программисты используют в своей практике язык ассемблера. Языки высокого уровня, такие как С и C++, поддерживаются практически на всех архитектурах и обеспечивают достаточно высокую производительность программ. Для тех редких случаев, когда требуется встроить в программу ассемблерные инструкции, в коллекции GNU-компиляторов (GCC) предусмотрены специальные средства, учитывающие особенности конкретной архитектуры.
Встроенными ассемблерными инструкциями следует пользоваться осторожно, так как они являются системно-зависимыми. Например, программу с инструкциями архитектуры x86 не удастся скомпилировать на компьютерах PowerPC. В то же время такие инструкции позволяют напрямую обращаться к аппаратным устройствам, вследствие чего программный код выполняется чуть быстрее.
В программы, написанные на языках С и C++, ассемблерные инструкции встраиваются с помощью функции asm(). Например, на платформе x86 команда
asm("fsin" : "=t" (answer) : "0" (angle));
является эквивалентом следующей инструкции языка C:[30]
answer = sin(angle);
Обратите внимание на то, что, в отличие от обычных ассемблерных инструкций, функция asm() позволяет указывать входные и выходные операнды, используя синтаксис языка С.
Подробнее узнать об инструкциях архитектуры x86, используемых в настоящей главе, можно по следующим адресам: http://developer.intel.com/design/pentiumii/manuals и http://www.x86-64.org/documentation.
9.1. Когда необходим ассемблерный код
Инструкции, указываемые в функции asm(), позволяют программам напрямую обращаться к аппаратным устройствам, поэтому полученные программы выполняются быстрее. Ассемблерные инструкции используются при написании кода операционных систем. Например, файл /usr/include/asm/io.h содержит объявления команд, осуществляющих прямой доступ к портам ввода-вывода. Можно также назвать один из исходных файлов ОС Linux — /usr/src/linux/arch/i386/kernel/process.s; в нем с помощью инструкции hlt реализуется пустой цикл ожидания.
Прибегать к ассемблерным инструкциям как к средству ускорения работы программы следует лишь в крайнем случае. Современные компиляторы достаточно сложны и прекрасно осведомлены об особенностях работы процессоров, для которых они генерируют код. Часто они создают цепочки кодов, которые кажутся неэффективными или неоптимальными, но на самом деле такие последовательности инструкций выполняются быстрее других. В подавляющем большинстве случаев можно положиться на оптимизирующие способности компиляторов.
Иногда одна или две ассемблерные команды способны заменить целую группу высокоуровневых инструкций. Например, чтобы определить позицию самого старшего значащего бита целого числа в языке С, требуется написать цикл, тогда как во многих ассемблерных языках для этой цели существует операция bsrl. Ее использование будет продемонстрировано в разделе 9.4, "Пример".