我在调试多线程 C 应用程序时遇到了非常困难的事情,我已经对其进行了一些更改。我一直无法使用 GDB 来帮助确定问题(有关详细信息,请参见下面的代码)。
以下代码来自在其自己的线程中打开的任务之一。我已经删除了问题之后的大部分代码。
void tskProcessTenMinuteTables(void *input)
{
/* Check the minute as soon as we start. If we're started on a ten min
* boundary, sleep for one minute.
*/
time_t now;
time_t wakeup;
struct tm *next_tick_ptr;
now = time(NULL);
next_tick_ptr = localtime(&now);
/* returns a time struct populated w/ next ten min boundary */
GetNextTenMinBoundary(next_tick_ptr);
wakeup = mktime(next_tick_ptr);
sleep(2); /* Without this sleep, the following if() was always true. */
if(next_tick_ptr->tm_min % 10 == 0)
{
fprintf(stderr, "On tenmin boundary on initialization.. task sleeping for 60 seconds.\n");
/* debug statements to test the cause of segfault. */
fprintf(stderr, "NOM NOM NOM\n");
printf( "Test%d\n", 1);
fprintf(stderr, "Test%d\n", 2); /* <~~~ This statement is the guilty party */
sleep(60);
}
/* Main loop. Every loop besides the tick itself will consist only
* of a call to time and a comparison of current stamp with wakeup.
* this should be pretty light on the processing side.
*
* Re-implement this as a sleep/awake with a signal in the future.
*/
while(1)
{
now = time(NULL);
if( now >= wakeup )
{
fprintf(stderr, "Triggered 1.\n");
fprintf(stderr, "Triggered 2.\n");
char statement[150];
fprintf(stderr, "Triggered 3.\n");
sprintf(statement, "SELECT ten_min_end(%d::int2)",GetTenMinPeriodNumber());
fprintf(stderr, "Triggered 4.\n");
DBCallStoredProcedure(statement);
fprintf(stderr, "Triggered 5.\n");
}
}
原因是试图将 fprintf 与可变参数(?)一起使用。除了模式有效之外,没有任何东西调用它。带或不带参数的 Printf 函数。
fprintf(stderr, "Hi #%d.\n", 1); <~~ segfault
fprintf(stderr, "Hi #1.\n"); <~~ works
printf("Hi #%d.\n", 1); <~~ works
printf("Hi #1.\n"); <~~ works
在 gdb 中运行时,在 gdb 变得无响应之前,我收到以下消息。需要 kill -9 才能终止。
$gdb ir_client
(gdb) r
Starting program: /home/ziop/Experimental_IR_Clients/ir-10-20/IR_Client/obj-linux-x86/ir_client
[Thread debugging using libthread_db enabled]
[New Thread 0xb7fe5b70 (LWP 32269)]
[New Thread 0xb7fc4b70 (LWP 32270)]
(032266 - -1208067216) 20-Oct-2010 10:56:19.59 - IR_Client_ConnectCmdPort - Socket connected.
[New Thread 0xb7ffdb70 (LWP 32272)]
(032266 - main thread) 20-Oct-2010 10:56:19.59 - sl_exit - Exiting thread with code 0.
On tenmin boundary on initialization.. task sleeping for 60 seconds.
NOM NOM NOM
Test1
我是 C 语言的新手,所以这可能是显而易见的。我的第一个想法是无缓冲输出不是线程安全的,但如果没有传递变量,fprintf 总是成功。 Pthread funkiness 仍然是我的首要怀疑对象。不幸的是,我暂时坚持使用架构。
提前致谢。
最佳答案
第一步是尝试在不引入线程的情况下运行函数。只需编写一个包含 main
的 .c 文件,该文件会执行最低限度的操作以准备启动线程,然后它不会执行此操作,而是调用该函数。如果仅用一个线程即可重现问题,则调试起来会容易得多。
另外,如果你使用的是 gcc,你应该编译:
-fstack-protector-all -Wstack-protector -fno-omit-frame-pointer
除了你的正常标志(至少在你发现问题之前)。这些将有助于调试并可能在编译时发出更多警告。我假设您知道 -O
标志如何影响调试能力和功能(特别是如果您已经在 C 代码中做错或未定义的事情)。
当您在 GDB 中并且事情看起来像是被锁定或程序需要很长时间来做某事时,您通常可以按 CTRL Z
返回到 (gdb)
而不终止程序。这会向程序发出停止信号,让您再次与 GDB 交互,这样您就可以了解程序实际在做什么。
编辑
我显然在评论讨论中解决了这个问题,所以我会在这里写下问题所在。
快速浏览一下代码并没有发现会导致段错误(非法内存访问)的问题,Zypsy(OP)告诉我该函数在直接从 main 调用而不是运行时运行良好通过一个单独的线程。
Valgrind 报告线程的堆栈空间无法扩展到某个地址。在 Linux 中,主线程的堆栈以一种可以轻松增长的方式映射到应用程序中,但在为线程堆栈分配内存时通常不会这样做。
我要求 Zypsy(OP)插入一些代码,打印出线程堆栈中已知低地址的地址(printf("thread stk = %p\n", &input);
) 以便该值可以与失败消息中给出的地址进行比较。由此我可以猜测堆栈大小。这并不表明在线程函数的开始和失败之间消耗了太多的堆栈空间,但对于问题中的代码来说,空间似乎也不算太小(尽管事实证明它显然太小了)。
因为 pthread_create
函数允许您接受线程属性的设置(传入 NULL
)或传入指定线程各种设置的参数 I问是否可以发布调用 pthread_create
的代码,以便我查看是否有任何可疑设置。
在查看这段代码后(围绕各种 pthread_
函数的应用程序特定包装器)我看到实际上设置了一些与堆栈相关的属性。我要求 OP 查看对该函数的调用并查找与堆栈分配方式相关的可疑内容(确保大小值和分配的内存大小实际上相同)。事实证明,OP 然后发现该线程的堆栈被分配得比其他线程的堆栈小。毕竟堆栈太小了。
关于c - 使用 fprintf 的奇怪 SEGFAULTS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3980687/