Модераторы: xvr
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> signal & setjmp / longjmp, Что я делаю не так? 
V
    Опции темы
null56
Дата 19.3.2008, 19:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



Проблема возникла не у меня,  у моего коллеги... и конечно я посоветовал ему лучше отладить свой код... но он ни в какую...
Задача состоит в том, чтобы отловить segmentation fault и не вылетать из программы, а просто проигнорировать этот момент и продолжить со следующей итерации...

Вчера познакомился с макросами setjmp и longjmp, но их работа по видимому осталась для меня загадкой... 

Приведу пример кода, над которым я ставлю эксперименты

Цитата

#include <stdio.h> 
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
//-
void jumper();
void eventHandler(int);
//-
jmp_buf env;
//-
int main()
{
    
    signal(SIGSEGV, eventHandler);    
    while (true)
    {
  jumper();
  sleep(5);
    }
    return 0;    
}

void jumper()
{
    char *pTest = NULL;
    if (setjmp(env) == 0)
    {
  // SEGMENTATION FAULT
  *pTest = 123;
    }
    else
    {
  // IGNORE FAULT
  pTest = NULL;
    }
}

void eventHandler(int sig)
{
// ЭТО ТАК... ТЕСТ
    //signal(sig, eventHandler);

// ВОСТАНОВЛЕНИЕ КОНТЕКСТА СОБСТВЕННО
    longjmp(env, 1);
}



Первая итерация отрабатывает, как надо... Но на второй, судя по дебагеру теряется адрес обработчика сигнала и программа вылетает на этапе генерации прерывания, а именно на строке *pTest = 123;

Объясните пожалуйста, где моя ошибка... Судя по всему я просто не правильно понимаю работу этих макросов...

Заранее благодарен всем откликнувшимся за помощь...
PM MAIL   Вверх
creatorcode
Дата 19.3.2008, 19:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 82
Регистрация: 12.6.2006
Где: Ленинград

Репутация: нет
Всего: 4



Код

char *pTest = NULL;
*pTest = 123;

pTest - нулевой указатель, поэтому и вылетает с ошибкой. Вы должны сначала выделить память под указатель и только потом использовать его. Например так:
Код

pTest=(char*)malloc(size);

где size - размер выделяемой памяти. В вашем случае по всей видимости size=1. Хотя тогда непонятно с какой вообще целью используется указатель.
PM MAIL   Вверх
MAKCim
Дата 19.3.2008, 19:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



не вижу ошибки
у меня все сводится к бесконечному циклу, т. е как и должно быть

Добавлено через 44 секунды
creatorcode, 
вы не в теме, извините


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
creatorcode
Дата 19.3.2008, 20:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 82
Регистрация: 12.6.2006
Где: Ленинград

Репутация: нет
Всего: 4



Прошу прощения, не сразу въехал в вопрос. Попробую оправдаться smile 
Код

#include <stdio.h> 
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
//-
void jumper();
void eventHandler(int);
//-
jmp_buf env;
//-
int main()
{
    
    signal(SIGSEGV, eventHandler);    
    while (true)
    {
  jumper();
  sleep(5);
    }
    return 0;    
}

void jumper()
{
    char *pTest = NULL;
    if (setjmp(env) == 0)
    {
  // SEGMENTATION FAULT
  *pTest = 123;
    }
    else
    {
  // IGNORE FAULT
  pTest = NULL;
    }
longjmp(env, 1);//!!!!!!!!!!!!!!!!!!!!!
}

void eventHandler(int sig)
{
// ЭТО ТАК... ТЕСТ
    //signal(sig, eventHandler);

// ВОСТАНОВЛЕНИЕ КОНТЕКСТА СОБСТВЕННО
    longjmp(env, 1);
}


К тому же не вижу смысла использовать signal. Можно вполне обойтись и без него:
Код

#include <stdio.h> 
#include <setjmp.h>
void jumper();
jmp_buf env;
int main()
{
    while (true)
    {
     jumper();
    sleep(5);
    }
    return 0;    
}

void jumper()
{
    char *pTest = NULL;
    if (setjmp(env) == 0)
    {
     // SEGMENTATION FAULT
     *pTest = 123;
    }
    else
    {
    // IGNORE FAULT
     pTest = NULL;
    }
    longjmp(env, 1);//!!!!!!!!!!!!!!!!!!!!!
}


Это сообщение отредактировал(а) creatorcode - 19.3.2008, 20:14
PM MAIL   Вверх
null56
Дата 19.3.2008, 20:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



MAKCim, 

Вы попробовали запускать этот код?
Потому что у меня там ожидание стоит между циклами 5 секунд, а ошибка вылетает на ВТОРОЙ (!!!) итерации цикла?
Приведу пример код с отладочной печатью...

Код

#include <stdio.h> 
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
//-
void jumper();
void eventHandler(int);
//-
jmp_buf env;
//-
int main()
{
    signal(SIGSEGV, eventHandler);    
    while (true)
    {
        printf("Before\n");
        jumper();
        sleep(5);
        printf("After\n");
    }
    return 0;    
}

void jumper(/*jmp_buf &env*/)
{
    char *pTest = NULL;
    if (setjmp(env) == 0)
    {
        printf("Bad\n");
        *pTest = 123;
    }
    else
    {
        printf("Good\n");
        pTest = NULL;
    }
}

void eventHandler(int sig)
{
    printf("Event\n");
    longjmp(env, 1);
}



Результатом работы будет:
Цитата

Before
Bad
Event
Good
After
Before
Bad

PM MAIL   Вверх
null56
Дата 19.3.2008, 22:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



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

Код

#include <stdio.h> 
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
//-
void jumper();
void userHandler();
//-
jmp_buf env;
//-
int main()
{
    while (true)
    {
        printf("Before\n");
        jumper();
        sleep(5);
        printf("After\n");
    }
    return 0;    
}

void jumper()
{
    char *pTest = NULL;
    if (setjmp(env) == 0)
    {
        printf("Bad\n");
        userHandler();
    }
    else
    {
        printf("Good\n");
    }
}

void userHandler()
{
    printf("User\n");
    longjmp(env, 1);
}


В чем хитрость... в какой области памяти создается функция, которая назначается методом signal?
PM MAIL   Вверх
MAKCim
Дата 19.3.2008, 23:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



null56, 
вообщем, все понятно
сначала решение
чтобы все заработало, нужно использовать sigsetjmp() и siglongjmp()
теперь почему не работало
следите за логикой
1. при формировании ядром сигнального фрейма, в стек записывается адрес возврата
т. е когда выполняется код обработчика SIGSEGV на вершине стека лежит адрес возврата
при вызове signal() мы не можем его явно указать, поэтому код libc, которому соответствует функция signal()
устанавливает его за нас
по-умолчанию адрес возврата, указываемый в libc, соответствует внутренней функции libc, которая осуществляет восстановление контекста процесса исходя из данных, которые ядро сохранило для нас в сигнальном фрейме
2. на первой итерации SIGSEGV успешно перехватывается и управление переходит на наш обработчик
но вместо того, чтобы перейти по адресу, находящемуся на вершине стека, идет вызов longjmp(), который полностью восстанавливает контекст процесса
важно отметить, что ядро перед вызовом обработчика сигнала блокирует этот сигнал
поэтому после longjmp() мы оказываемся тут
Код

else
    {
        printf("Good\n");
        pTest = NULL;
    }

и при этом SIGSEGV заблокирован
3. генерация сигнала для процесса в результате #PF осуществляется в функции ядра force_sig_info()
вот ее код
Код

int
 force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
         unsigned long int flags;
         int ret, blocked, ignored;
         struct k_sigaction *action;
 
         spin_lock_irqsave(&t->sighand->siglock, flags);
         action = &t->sighand->action[sig-1];
         ignored = action->sa.sa_handler == SIG_IGN;
         blocked = sigismember(&t->blocked, sig);
         if (blocked || ignored) {
                 action->sa.sa_handler = SIG_DFL;
                 if (blocked) {
                         sigdelset(&t->blocked, sig);
                         recalc_sigpending_tsk(t);
                 }
         }
         ret = specific_send_sig_info(sig, info, t);
         spin_unlock_irqrestore(&t->sighand->siglock, flags);
 
         return ret;
 }

ключевой момент зключается в том, что в ней идет проверка, не заблокирован ли генерируемый сигнал
и если заблокирован (а в нашем случае он заблокирован, как я показал выше), то он разблокируется с восстановлением дефолтного состояния обработчика сигнала, которым является SIG_DFL
для SIGSEGV дефолный обработчик приводит к завершению процесса
поэтому на второй итерации идет завершение процесса, а не передача управления на наш обработчик
sigsetjmp() сохраняет маску заблокированных к моменту вызова сигналов
(на первой итерации SIGSEGV еще не заблокирован)
siglongjmp() восстанавливает маску заблокированных сигналов
а значит после siglongjmp() SIGSEGV не будет заблокирован и в функции force_sig_info() не произойдет сброс нашего обработчика

Это сообщение отредактировал(а) MAKCim - 19.3.2008, 23:14


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
null56
Дата 20.3.2008, 01:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 4
Всего: 12



Огромное спасибо за помощь...
Я понял свою ошибку, она в большинстве своем все - таки была от незнания...

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

Еще раз спасибо всем откликнувшимся...

ЗЫ: 
MAKCim, я вопрос не по теме отправил вам в личку
PM MAIL   Вверх
xvr
Дата 20.3.2008, 13:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 20
Всего: 223



Цитата(null56 @ 20.3.2008,  01:08)
Огромное спасибо за помощь...
Я понял свою ошибку, она в большинстве своем все - таки была от незнания...

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

Еще раз спасибо всем откликнувшимся...

ЗЫ: 
MAKCim, я вопрос не по теме отправил вам в личку

Я бы еще добавил по поводу signal:

Цитата

Upon  arrival of a signal with number signum the following happens.  ... Finally, if the  handler  is  set to a function sighandler then first either the handler is reset to SIG_DFL or an implementation-dependent blocking of the signal is performed and next sighandler is called with argument signum.
...
The original Unix signal() would reset the handler to SIG_DFL, and System  V  (and the Linux kernel and libc4,5) does the same.  On the other hand, BSD does not reset the handler, but blocks new instances of  this signal from occurring during a call of the handler.  The glibc2 library follows the BSD behaviour.


PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Программирование под Unix/Linux"
xvr
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой "Код".
  • Вопросы мобильной разработки тут
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

 
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема »


 




[ Время генерации скрипта: 0.0577 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.