【问题标题】:fscanf causes segmentation faultfscanf 导致分段错误
【发布时间】:2019-01-15 22:33:53
【问题描述】:

我正在编写一个代码来计算父进程为init 的进程数。调用 fork 并让子级使用 exec 函数并将其输出通过管道传递回父级。在那方面一切似乎都很好,但是当我在管道的父级读取端使用fdopen,然后使用fscanf 时,程序崩溃了,即使FILE 流不是NULL。我对每个函数调用进行了检查。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

static void     fatalError(char *message);

int main(int argc, char *argv[])
{

    int     total, initCount = 0;
    int     pipeToParent[2];
    pid_t   pid;
    FILE    *file;



    if (pipe(pipeToParent) < 0)
        fatalError("pipe() error");

    if ((pid = fork()) < 0)
        fatalError("fork() error");
    else if (pid == 0) {

        if (close(pipeToParent[0]) < 0)
            fatalError("close() error");

        if (dup2(pipeToParent[1], STDOUT_FILENO) < 0)
            fatalError("dup2() error");

        execlp("ps", "ps", "ahxo", "ppid", NULL);
        fatalError("exec() error");

    }


    if (close(pipeToParent[1]) < 0)
        fatalError("close() error");

    wait(NULL);

    if ((file = fdopen(pipeToParent[0], "r")) == NULL)
        fatalError("fdopen() error");


    for (total = 0; fscanf(file, "%d", &pid) != EOF; total++)
        if (pid == 1)
            initCount++;

    if (fclose(file) < 0)
        fatalError("fclose() error");

    printf("%.2f%%\n", (float) initCount * 100 / total);
    exit(EXIT_SUCCESS);


}


static void     fatalError(char *message) {
    perror(message);
    exit(EXIT_FAILURE);
}

运行 GDB 给出了这个:

Program received signal SIGSEGV, Segmentation fault.
__isoc99_fscanf (stream=0x55757260, format=0x555555554c44 "%d") at isoc99_fscanf.c:30
30  isoc99_fscanf.c: No such file or directory.

还有 Makefile 警告:

gcc -std=c11 -g -Wall -pedantic    init.c   -o init
init.c: In function ‘main’:
init.c:55:14: warning: implicit declaration of function ‘fdopen’; did you mean ‘fopen’? [-Wimplicit-function-declaration]
  if ((file = fdopen(pipeToParent[0], "r")) == NULL)
              ^~~~~~
              fopen
init.c:55:12: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
  if ((file = fdopen(pipeToParent[0], "r")) == NULL)
            ^

【问题讨论】:

  • isoc99_fscanf.c 在第 30 行有一些宏,但不知道是不是那个版本。而且它似乎不会失败。为什么 glibc 中包含调试信息?你在运行什么操作系统?你有什么 glibc 版本?顺便提一句。 fscanf(file, "%d", &amp;pid) 成功返回 1,你应该忽略它返回 0 的情况。
  • 我确实收到了一个警告,询问我是否指的是 fopen 而不是 fdopen,不确定这是否有帮助,但我当然不是指前者
  • 您是否收到警告说该函数是隐式声明的? warning: implicit declaration of function ...
  • OT:如果 exec*() 函数在 fork() 之后失败,您几乎肯定不想在之后调用 exit() - 这会将所有从父地址空间复制的缓冲数据刷新到将孩子的地址空间放入父母的文件中。它还将调用父进程注册的所有退出处理程序。 _exit() 存在是有原因的。
  • OT:关于:int main(int argc, char *argv[])main()的参数不打算使用时,使用签名:int main( void )

标签: c segmentation-fault


【解决方案1】:

在 C 中,如果不存在先前的函数声明,则假定该函数返回 int。如果编译器假定函数返回int 而它返回FILE * 并且如果sizeof(FILE*) &lt; sizeof(int) 返回值被截断并且无效。因此,当传递给 fscanf 的指针被截断且无效时,您会收到内部 glibc 错误。

来自man fdopen你可以阅读:

fdopen(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

这些是您需要定义的宏,以便在您的程序中声明 fdopen()。您需要在 任何包含之前定义它们。我通常只定义_GNU_SOURCE,它在features.h 中定义_POSIX_C_SOURCE 等。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

.. rest of the program ...

有关更多信息,请参阅future test macros

当使用gcc 时,您也可以使用gcc -std=gnu11 或其他-std=gnu*-std=c11-std=gnu11 相同,只是在编译时预定义了宏 _GNU_SOURCE

【讨论】:

    【解决方案2】:

    根据Linux man page for fdopen(),Linux/glibc 需要定义_POSIX_C_SOURCE 宏:

    glibc 的功能测试宏要求(请参阅 feature_test_macros(7)):

       fdopen(): _POSIX_C_SOURCE
    

    否则,您会得到一个隐式声明,如前所述,这意味着假定函数返回int。而fdopen() 实际返回的指针很可能不适合int,所以它被截断了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-20
      • 2011-07-19
      • 2017-03-29
      • 2015-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      • 2020-12-31
      相关资源
      最近更新 更多