LINUX.ORG.RU

[СИ] Стивенс. Не понимаю.

 


0

1

[СИ] Стивенс. Не понимаю.

Язык СИ ОС UNIX

В книге «UNIX Взаимодействие процессов»
(русская, бумажная) приведена
упрощенная реализация очередей сообщений POSIX.
Параграф 5.8. Там используется контрольное число
MQI_MAGIC, определенное define-ом. Вот оно-то и
не понятно. Вот фрагменты этих функций:


Листинги 5.17 - 5.19.

7   mymqd_t
8   mymq_open(const char *pathname, int oflag, ...)
9   {
          ....
64          /* Выделение структуры mymq_info{ } для очереди */
65      if( (mqinfo = malloc(sizeof(struct mymq_hdr))) == NULL)
66          goto err;
67      mqinfo->mqi_hdr = mqhdr = (struct mymq_hdr *) mptr;
68      mqinfo->mqi_magic = MQI_MAGIC;
          ....
107     return((mymqd_t) mqinfo);
159 }


Листинг 5.20.

3   int
4   mymq_close(mymqd_t mqd)
5   {
	      ....
11      if(mqinfo->mqi_magic != MQI_MAGIC){
12          errno = EBADF;
13          return(-1);
14      }
          ....
24      mqinfo->mqi_magic = 0;   /* на всякий случай */
25      free(mqinfo);
26      return(0);
27  }

В функции mymq_open(); инициируется поле magic, а
во всех других функциях есть проверка, как в mymq_close();
То, что структура выделяется malloc-ом, я понимаю
так: структура должна существовать и при возврате
из mymq_open(), значит, локальная не годится.
Ну а что-же magic? От него-то какая польза?
Я думал, что это защита от ошибок программиста,
чтоб процесс не падал.
Но когда разобрался, то ничего не понял.
Я не тестил, теоретически разбирался.
Процессы не родственные (а хоть бы и родственные),
однопотоковые. Если всё написать правильно, то и без
magic все сработает хорошо. А если допустить ошибку,
то всё равно не поможет. Например, забыл написать
mqd = mymq_open(); и вызываю другие функции очереди.
Падение. Ошибочно дважды вызвал mymq_close(); Опять
падение. Ведь первый mymq_close(); освободит память,
а второй уже полезет в никуда (память-то уже освобождена).

В чем тут дело?

Кто знает прошу ответить.


Я тоже Стивенса читал, но на его функции-обертки внимания не обращал. Очереди создавал так, к примеру:

void mk_queues(){
        attr.mq_maxmsg = 256; // устанавливаем 256 сообщений в очереди
        attr.mq_flags = O_NONBLOCK;
        attr.mq_msgsize = 512; // максимальный размер одного сообщения
        mq_out = mq_open(OUT_QUEUE, O_WRONLY|O_CREAT|O_NONBLOCK, 00666, &attr);
        if(mq_out < 0){printf("Error opening out queue: %s\n", strerror(errno)); exit(1);}
        if(mq_getattr(mq_out, &attr) < 0){
                printf("Error getting queue attributes: %s\n", strerror(errno));exit(1);}
        mq_size = attr.mq_msgsize;
        if((out_buf= (char*)calloc(mq_size, 1)) == NULL){
                printf("Can't allocate memory: %s\n", strerror(errno));exit(1);}
        mq_size--; // оставляем место для завершающего нуля
}

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Я знаю, что эта книга на русском есть в интернете. Но я потерял ссылку. Если кто знает, дайте, пожалуйста, ссылку.

oleg_2
() автор топика

Я процитирую вот отсюда:

Поле mqi_magiс принимает значение MQI_MAGIC в момент инициализации структуры. Это значение проверяется всеми функциями, которым передается указатель типа mqd_t, что дает им возможность удостовериться, что указатель действительно указывает на структуру типа mq_infо. mqi_flags содержит флаг отключения блокировки для открывшего очередь процесса.

Begemoth ★★★★★
()

Стандартный, между прочим, приём.

Я думал, что это защита от ошибок программиста, чтоб процесс не падал.


Это средство для выявления ошибок, а не для защиты от них. Правда, на кой чёрт он возвращает ошибку при невалидном magic'е - не очень понятно. Наиболее разумно - это assert(mqinfo->mqi_magic == MQI_MAGIC). В реальных условиях, введение такой небольшой избыточности в код существенно сокращает время, затрачиваемое на поиск багов.

fang
()
Ответ на: комментарий от fang

Спасибо. Цитату я нашел и в своей книге.
Но не понятно. Усли окажется, что указатель показывает
на доступную память при вызове, то еще куда ни шло.
Выдаст -1. Но ведь он может показывать в недоступную
память, и тогда падение.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

> Но ведь он может показывать в недоступную память, и тогда падение.

А тут уж ничего не поделаешь. Не, ну можно поймать SEGV и сделать siglongjmp, но хитрый программист найдёт другой способ выстрелить себе в ногу.

Поддерживаю fang: если проверки и делать, то при обнаружении ошибки надо сразу резко падать. И в релизной сборке все проверки и поле mqi_magic должны быть отключены.

const86 ★★★★★
()
Ответ на: комментарий от oleg_2

> Усли окажется, что указатель показывает на доступную память при вызове, то еще куда ни шло.

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

Но ведь он может показывать в недоступную память, и тогда падение


Может. От падения ещё никто не умирал. Этот случай гораздо проще с точки зрения поиска бага - при возникновении фолта ты уже будешь знать, в какую сторону тебе следует копать.

fang
()
Ответ на: комментарий от const86

И в релизной сборке все проверки и поле mqi_magic должны быть отключены.

а вдруг в релизе ошибка... Я оставляю ассерты, они много не жрут.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

> а вдруг в релизе ошибка... Я оставляю ассерты, они много не жрут.

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

anonymous
()
Ответ на: комментарий от anonymous

насколько я знаю, в релизе ассерт в нормальных системах сборки превращается в ничто. И какие эксепшны в Си?

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

пусть лучше глючит, но продолжает работать.

Там где у мну обычно стоят ассерты дальше будет или segfault или повреждение памяти => рандомные падения и непредсказуемое поведение вплоть до повреждения данных если, скажем, используется sqlite. Мне как-то не очень хочется чтобы такая программа продолжала работать. Но всё, конечно, от ситуации зависит, для какой-нить игрушки это побарабану.

true_admin ★★★★★
()
Ответ на: комментарий от DELIRIUM

ассерт в нормальных системах сборки превращается в ничто.

поэтому у меня свой ассерт который не зависит от NDEBUG :)

И какие эксепшны в Си?

ну тут просто речь шла без привязки к конкретному ЯП.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

> поэтому у меня свой ассерт который не зависит от NDEBUG :)

Ну тут спорный вопрос. Я встречал проекты, где стоял такой ассерт на количество ресурсов, один хрен освобождаемых ОС, при это ассерт стоял на этапе деинициализации системы, и в итоге пользователь получал вместо обычного завершения программы с автоматическим освобождением всех дескрипторов SIGABRT и коредамп.

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от DELIRIUM

Ну тут спорный вопрос.

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

true_admin ★★★★★
()
Ответ на: комментарий от Eddy_Em

> Я встречал проекты, где стоял такой ассерт на количество ресурсов, один хрен освобождаемых ОС, при это ассерт стоял на этапе деинициализации системы, и в итоге пользователь получал вместо обычного завершения программы с автоматическим освобождением всех дескрипторов SIGABRT и коредамп.

А у меня пользователь получает segfault и понятия не имеет, в чем дело :)


специальную олимпиаду можно считать открытой :)

Corey
()
Ответ на: комментарий от beastie

В данном случае до ассерта дело не дойдет, по моему.
Так же, как и до return(-1);
Программа рухнет раньше. А ошибки можно проверять и
обычным способом. Вот если бы был способ
заранее, не обращаясь еще по указателю, узнать,
не показывает ли этот указатель за пределы, тогда
вместе с этим magic-ом получилось бы безопасно.
Когда пишем closedir() в той ветке программы, где
не следует, то получаем то же самое - сегфолт.

beastie
«assert(mqinfo->mqi_hdr)»
Но ведь указатель может быть и не нулевой, например,
при повторном close. А все равно дефолтный.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

> Вот если бы был способ заранее, не обращаясь еще по указателю, узнать, не показывает ли этот указатель за пределы

В x86 есть команды verr и verw. Портабельный способ - ловить сигналы.

const86 ★★★★★
()

1. Если комплексно заботиться о внятной диагностике ошибок, то перед if(mqinfo->mqi_magic != MQI_MAGIC) должна еще стоять проверка на !mqinfo. Конечно, NULL только частный случай невалидного указателя, но встречается он на порядки чаще остальных. Поэтому его тоже надо ловить.

2. Возвращать код ошибки или падать с ассертом, выматерившись на stderr — зависит от данной конкретной функции и её предназначения, а также от рода ошибки. В данном случае, имхо, лучше падать.

3. Ни в коем случае нельзя подвергать опасности данные пользователя. При обнаружении ошибок разряда «этого не может быть» (а ассерты охраняют именно такой вид ошибок) программа должна тут же завершаться, предварительно выплюнув на stderr внятное диагностическое сообщение. Продолжение работы сбойной программы может быть абсолютно не предсказуемым: от банального сегфолта до коммита случайного мусора в базу данных. Ни в коем случае нельзя убирать ассерты из релизной сборки. Ассерты — не средство отладки, а способ удостовериться в валидности данных и логической непротиворечивости кода. Кроме то, даже с данными пользователя если ничего не случится и программа просто вылетт сегфолтом, искать ошибки по багам в стиле «ВНЕЗАПНО сегфолт» программисту будет оооочень весело.

anonymous
()
Ответ на: комментарий от beastie

> но не проще ли просто assert(mqinfo->mqi_hdr) ? эффект тот же

Думаю, не тот же. Какова вероятность, что при передаче указателя на данные, не являющиеся структурой нужного типа, это поле будет равно нулю?

fang
()
Ответ на: комментарий от true_admin

Не докачает дозу лекарства и привет.
Не начнет качать дозу лекарства и привет.

Старая бородатая анекдота в тему:
- Ну как дома?
- Спасибо, хорошо. Все живы-здоровы.
- Теща как?
- С тещей вообще прекрасно. Вчера картошку чистила, палец порезала. Пришлось пристрелить, чтобы не мучалась.

vasily_pupkin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.