c - 使用 fprintf 的奇怪 SEGFAULTS

标签 c multithreading gdb pthreads printf

我在调试多线程 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/

相关文章:

ruby-on-rails - Rails i18n 方法导致段错误?

c - NOP 雪橇如何工作?

c - 读取文件然后重写它。 C 密码学程序

java - “java.lang.UnsupportedOperationException: Not supported yet.”

linux - gdb coredump - 调用函数或继续执行

c++ - 防止 GDB 中的 PLT(过程链接表)断点

c - 使链表的节点成为最后一个节点的功能?

c# - 有没有更好的方法来等待排队的线程?

JavaFX UI 在事件监听器的 JavaFX 应用程序线程中卡住,但可与 Platform.runLater 一起使用

linux - GDB在远程调试期间挂起,库版本不匹配