Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Qt и кроссплатформенное программирование С/С++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 06.04.2009, 20:00   #1
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию Уроки по C в Linux: Сигналы

Итак, это первый из 6 уроков по программированию на C(именно на С а не на C++) в Linux.
Этот урок посвящен механизму сигналов в Linux.
Как известно сигнал - это способ передачи данных (ну не совсем) между процессами. Отличительная особенность сигнала в том, что посылать/принимать сигналы можно только в пределах одного компьютера. По сети или интернет использовать сигналы нельзя. Отличительная особенность сигналов в том, что их обработка происходит сразу после отправки/получения (или не происходит вовсе). Есть два типа сигналов. Классические (31 шт.) и реального времени. В данном уроке я расскажу только о некоторых классических сигналах. Об остальных вы можете прочитать в книге Майкл. К. Джонсон и Эрик В. Троан - "Разработка приложений в среде Linux" (Глава 12 - Обработка сигналов) Скачать

Сигналы (классические):
Сигнал SIGHUP (номер 1) изначально был предназначен для того, чтобы информировать программу о потере связи с управляющим терминалом (терминалы часто подключались к системе с помощью модемов, так что название сигнала происходит от hung up – повесить трубку). Сигнал SIGHUP посылается приложению так же и в том случае, если процесс-лидер сессии завершил свою работу. Многие программы-демоны, у которых нет лидера сессии, так же обрабатывают этот сигнал. В ответ на получение SIGHUP демон обычно перезапускается (или просто повторно читает файл конфигурации). По умолчанию программа, получившая этот сигнал, завершается.
Сигнал SIGINT (номер 2) обычно посылается процессу, если пользователь терминала дал команду прервать процесс (обычно эта команда – сочетание клавиш Ctrl-C) .
Сигнал SIGABRT (номер 6) посылается программе в результате вызова функции abort(3). В результате программа завершается с сохранением на диске образа памяти.
Сигнал SIGKILL (номер 9) завершает работу программы. Программа не может ни обработать, ни игнорировать этот сигнал.
Сигнал SIGSEGV (номер 11) посылается процессу, который пытается обратиться к не принадлежащей ему области памяти. Если обработчик сигнала не установлен, программа завершается с сохранением на диске образа памяти.
Сигнал SIGTERM (номер 15) вызывает «вежливое» завершение программы. Получив этот сигнал, программа может выполнить необходимые перед завершением операции (например, высвободить занятые ресурсы). Получение SIGTERM свидетельствует не об ошибке в программе, а о желании ОС или пользователя завершить ее.
Сигнал SIGCHLD (номер 17) посылается процессу в том случае, если его дочерний процесс завершился или был приостановлен. Родительский процесс также получит этот сигнал, если он установил режим отслеживания сигналов дочернего процесса и дочерний процесс получил какой-либо сигнал. По умолчанию сигнал SIGCHLD игнорируется.
Сигнал SIGCONT (номер 18) возобновляет выполнение процесса, остановленного сигналом SIGSTOP.
Сигнал SIGSTOP (номер 19) приостанавливает выполнение процесса. Как и SIGKILL, этот сигнал не возможно перехватить или игнорировать.
Сигнал SIGTSTP (номер 20) приостанавливает процесс по команде пользователя (обычно эта команда – сочетание клавиш Ctrl-Z).
Сигнал SIGIO/SIGPOLL (в Linux обе константы обозначают один сигнал – номер 29) сообщает процессу, что на одном из дескрипторов, открытых асинхронно, появились данные. По умолчанию этот сигнал, как ни странно, завершает работу программы.

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

Итак, начнем. ОС - Ubuntu Linux, компилятор - gcc.
Первый шаг - напишем функцию, которая будет порождать дочерний процесс.
Как известно, в Linux процессы порождаются функцией fork. Она клонирует текущий процесс. Чтобы сделать процесс, который выполняет другой код, нужно использовать функцию из семейства exec.
Код:
int copy;
copy = fork();
if(copy < 0)
	exit(1);	
if(copy == 0)
	int res = execl("child", "child", "", 0);
Разъясняю. fork - копирует процесс. Эта функция возвращает pid порожденного процесса. Если fork вернет значение меньше 0, то это значит что произошла ошибка, и продолжать нельзя. Нужно выйти.
Если fork вернет 0, это значит что процесс клонировался, и мы сейчас находимся в порожденном процессе. В данном случае мы вызываем функцию execl, которая назначает порожденному процессу новый исполняемый файл.
Код, который идет дальше - это код, который выполняет текущий процесс.

Дальше мы будем отправлять порожденному процессу сигналы. В качестве рабочего сигнала я выбрал SIGHUP.
Посылка сигнала выполняется с помощью функции kill(согласен, не сильно благозвучное название ).

Последний раз редактировалось MaTBeu; 07.04.2009 в 00:33.
MaTBeu вне форума Ответить с цитированием
Старый 06.04.2009, 20:05   #2
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию

//----------------------Продолжение----------------------------
Итак, код, идущий за предыдущим куском
Код:
printf(">Parent  --forked process pid %d\n", copy);
int i;
for(i = 0; i<10; i++)
{
	kill(copy, SIGHUP);
	sleep(1);
}
log_message(LOG_FILE, ">Parent  --send SIGTERM signal to child process\n");
kill(copy, SIGTERM);
signal(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
В цикле мы отправляем порожденному процессу 10 сигналов SIGHUP. Между сигналами задержка 1 секунда.
Дальше мы отправляем дочернему процессу сигнал SIGTERM - сигнал аварийного завершения.
Следующие 6 строк кода устанавливают обработчики для разных сигналов. Первый параметр функции signal - это сигнал, которому присваивается обработчик. Второй параметр - указатель на функцию-обработчик. Если второй параметр SIG_IGN, то сигнал игнорируется.
Мы устанавливаем обработчики только для двух сигналов - SIGHUP и SIGTERM. Обрабатывать их будет функция signal_handler.
Вот ее код
Код:
void signal_handler(int sig)
{
	switch(sig)
	{
		case SIGHUP:
			log_message(LOG_FILE, ">Parent  --hang up signal catched");
			break;
		case SIGTERM:
			log_message(LOG_FILE, ">Parent  --exit signal catched");
			exit(0);
			break;
	}
}
Кажется, все что касается сигналов, я рассказал.
Полный код родительского процесса Parent.c
Код:
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

#define LOG_FILE "/home/matbeu/prog/signal.log"
void exit(int);				//прототипы нужны чтобы компилятор не ругался
size_t strlen(char *);
int c = 0;

void log_message(char *filename, char *message)
{
	FILE *logfile;
	logfile = fopen(filename, "a");
	if(!logfile)
		return;
	fprintf(logfile, "%s\n", message);
	fclose(logfile);
}

void signal_handler(int sig)
{
	switch(sig)
	{
		case SIGHUP:
			log_message(LOG_FILE, ">Parent  --hang up signal catched");
			break;
		case SIGTERM:
			log_message(LOG_FILE, ">Parent  --exit signal catched");
			exit(0);
			break;
	}
}

void daemonize()
{
	int copy, i=0;
	copy = fork();
	if(copy < 0)
		exit(1);	
	if(copy == 0)
	{
		int res = execl("child", "child", "", 0);
	}
	printf(">Parent  --forked process pid %d\n", copy);
	for(i = 0; i<10; i++)
	{
		kill(copy, SIGHUP);
		sleep(1);
	}
	log_message(LOG_FILE, ">Parent  --send SIGTERM signal to child process\n");
	kill(copy, SIGTERM);
	signal(SIGCHLD, SIG_IGN);
	signal(SIGTSTP, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	signal(SIGTTIN, SIG_IGN);
	signal(SIGHUP, signal_handler);
	signal(SIGTERM, signal_handler);
}

main()
{
	printf("Parent process started\n");
	printf("Parent process pid %d\n", getpid());
	log_message(LOG_FILE, ">Parent --created");
	daemonize();
	while(1)
		sleep(1);
}
Полный код файла, который выполняется порожденным процессом Child.c
Код:
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

#define LOGFILE "/home/matbeu/prog/child.log"
void exit(int);
size_t strlen(char *);

void log_message(char *filename, char *message)
{
	FILE *logfile;
	logfile = fopen(filename, "a");
	if(!logfile)
		return;
	fprintf(logfile, "%s\n", message);
	fclose(logfile);
}

void signal_handler(int sig)
{
	switch(sig)
	{
		case SIGHUP:
			log_message(LOGFILE, ">child--hang up signal catched");
			printf(">Child --- Hang up signal catched\n");
			break;
		case SIGTERM:
			log_message(LOGFILE, ">child--exit signal catched");
			printf(">Child  --Parent pid %d send SIGTERM signal\n", getppid());
			kill(getppid(), SIGTERM);	//getppid() - функция возвращает pid родительского процесса, а getpid() - pid текущего процесса
			exit(0);
			break;
	}
}

void daemonize()
{
	signal(SIGCHLD, SIG_IGN);
	signal(SIGTSTP, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	signal(SIGTTIN, SIG_IGN);
	signal(SIGHUP, signal_handler);
	signal(SIGTERM, signal_handler);
}

main(int argc, char **cmd)
{
	int i;
	printf("Child process started\n");
	printf("Child process pid %d\n", getpid());
	log_message(LOGFILE, ">Child --started");
	daemonize();	
	while(1)
		sleep(1);
}

Последний раз редактировалось MaTBeu; 07.04.2009 в 00:28.
MaTBeu вне форума Ответить с цитированием
Старый 07.04.2009, 19:19   #3
Goblin
Форумчанин
 
Регистрация: 24.11.2007
Сообщений: 196
По умолчанию

Хорошая статья. Спасибо.
Буду ждать продолжения.
Goblin вне форума Ответить с цитированием
Старый 19.04.2012, 21:34   #4
savra
search money
Пользователь
 
Регистрация: 12.10.2009
Сообщений: 46
Восклицание

Здравствуйте, у меня такая проблема, когда компилирую программу, используя 2 .cpp файла из статьи, у меня происходят такие чудеса(постоянно разные результаты), вот бывает 3 варианта вывода:
1.

2.

3. Это нормальный результат, то что и должно выдавать


Подскажите пожалуйста в чем может быть проблема.

ЗЫ. использую Ubuntu 11.10 и QT 2.4.1

Последний раз редактировалось savra; 19.04.2012 в 21:55.
savra вне форума Ответить с цитированием
Старый 27.10.2014, 01:00   #5
serb345
Новичок
Джуниор
 
Регистрация: 27.10.2014
Сообщений: 1
По умолчанию

спс, полезно
serb345 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как принимать сигналы с ИК передатчика Вырвиглаз Компьютерное железо 7 29.03.2009 23:16
Нужны уроки beygul Свободное общение 0 26.10.2008 14:57
Мультимедийные уроки Linux Roman Linux (Ubuntu, Debian, Red Hat, CentOS, Mint) 2 24.10.2008 11:40
Linux на VMware....... WinXP <-> Linux DMUTPUU Операционные системы общие вопросы 1 24.09.2008 12:03