#c #sockets #gdb #pthreads #client-server
#c #сокеты #gdb #pthreads #клиент-сервер
Вопрос:
Я студент третьего курса колледжа по программной инженерии, изучаю операционные системы.
Я работал над клиент-серверным приложением чата на C, используя pthreads и сокеты. Я пытался использовать pthreads для обеспечения параллелизма обработки клиента без накладных расходов на разветвление. (Если это имеет значение, я разрабатываю на Ubuntu 11.04 x86).
Вместо того, чтобы хранить все в виде статических или глобальных / локальных переменных, я создал два структурированных типа данных, serverInfo_t
и clientInfo_t
, которые хранят мьютекс, дескрипторы файлов, флаг подключения и другую подобную информацию. Список клиентов реализован в виде простого односвязного списка, хранящегося в serverInfo_t
, который блокируется и разблокируется во время изменений.
При запуске серверного приложения я сначала вызываю createServer()
, который устанавливает сервер и, в конечном итоге, запускает дочерний поток, в обязанности которого входит прослушивание новых подключений. Эта функция возвращает указатель на недавно выделенный serverInfo_t
экземпляр, который затем передается в вызов createClient(serverInfo_t* pServer, int out_fd)
для создания клиента «admin», позволяя использовать сервер в качестве клиента сам по себе. Вот тут-то у меня и возникает проблема, поскольку, похоже, она вызывает segfault сразу после вызова createClient()
. (Немного урезанный код этого приведен ниже:)
int main(int argc, char** argv)
{
// ...
// Get listenPort from argv[1].
// Initialize the server.
serverInfo_t* pServer = createServer(listenPort);
// If createServer() failed, exit.
if (!pServer) {
fatal_error("ERROR: Unable to create server!"); // exit program
}
// Create and register the admin client.
clientInfo_t* pAdmin = createClient(pServer, STDOUT_FILENO); // Outputs directly to stdout
// ...
}
Во время отладки программы с помощью gdb я просматривал main () и заметил некоторые интересные результаты:
(gdb) n
22 serverInfo_t* pServer = createServer(listenPort);
(gdb) n
[New Thread 0xb7e53b70 (LWP 7986)]
25 if (!pServer) {
(gdb) n
30 clientInfo_t* pAdmin = createClient(pServer, FD_STDOUT);
(gdb) n
server4: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *)
amp;((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) amp;amp;
old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof
(struct malloc_chunk, fd_nextsize)) ((2 * (sizeof(size_t))) - 1)) amp; ~((2 *
(sizeof(size_t))) - 1))) amp;amp; ((old_top)->size amp; 0x1) amp;amp; ((unsigned long)old_end amp;
pagemask) == 0)' failed.
Program received signal SIGABRT, Aborted.
0xb7fe1424 in __kernel_vsyscall ()
(gdb) n
Single stepping until exit from function __kernel_vsyscall,
which has no line number information.
[Thread 0xb7e53b70 (LWP 7986) exited]
Program terminated with signal SIGABRT, Aborted.
The program no longer exists.
(gdb)
Что здесь происходит? Мне кажется, что программа полностью пропустила вызов createServer(), хотя проверка на НУЛЬ прошла. GDB отказалась перейти к функции createServer() или ее просто обошли из-за какой-то проблемы с оптимизацией компилятора? Если это так, то что с [New Thread 0xb7e53b70 (LWP 7986)]
выводом? И что, черт возьми, вызывает неудачное утверждение в malloc.c
?
Я был бы признателен за любую помощь по этому вопросу, которую вы можете предоставить. Пожалуйста, дайте мне знать, если есть проблема с моей методологией или с информацией, которую я предоставил. Я готов опубликовать свой полный Makefile и исходный код, если необходимо, хотя и с некоторыми изменениями (т. Е. для запуска Makefile необходимо изменить переменную Makefile ($(OUTDIR)).
РЕДАКТИРОВАТЬ: Я думаю, что нашел причину проблемы.
В createClient()
:
// ...
clientInfo_t* client = (clientInfo_t*)malloc(sizeof(clientInfo_t*)); // Accidental typo.
Исправлено:
// ...
clientInfo_t* client = (clientInfo_t*)malloc(sizeof(clientInfo_t));
Да, это исправлено. Я приношу извинения за пустую трату времени, но спасибо всем за помощь.
Комментарии:
1. #определить FD_STDOUT (0) ? stdout — это дескриптор файла 1 или использовать STDOUT_FILENO из заголовка <unistd.h>. Кроме того, вы отлаживаете код, скомпилированный с включенной оптимизацией? Обратите внимание, что выполнение
n
команды gdb не приводит к запуску функций, используйтеs
для запуска функций2. о, упс. Это, вероятно , помогло где-то еще, но по-прежнему выдает мне то же сообщение о сбое / ошибке. Что касается оптимизации, единственным флагом, который я передаю (помимо -pthread), является -ggdb , и если это означает, что он оптимизируется, то я хотел бы знать, как это исправить. И спасибо за совет по gdb, у меня действительно пока нет большого опыта в этом, и я посмотрю на это.
3. Я бы также попробовал обратную трассировку (
bt
).4. @Shotgun Ninja: нужно просмотреть больше кода из (или вы должны перейти в)
createClient()
и лучше локализовать, где он неисправен.5. Однако вам следует научиться использовать valgrind для будущей отладки, это сэкономит вам много времени.
Ответ №1:
Это похоже на ошибку памяти. Попробуйте запустить его через valgrind: valgind --tool=memcheck [COMMAND]
Для получения дополнительной информации перейдите на веб-сайт Valgrind.
Ответ №2:
Что здесь происходит?
Это:
malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) ...
является классической сигнатурой повреждения кучи (двойной free()
, запись после конца malloc()
редактируемого блока и т.д.).
Инструментом для поиска таких ошибок в Linux является Valgrind. Используйте его часто, это сэкономит вам бесчисленные часы отладки.