C - FSNF 导致分段错误



我正在编写一个代码,该代码将计算父进程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.

和生成文件警告:

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)
            ^

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

从男人 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 ...

有关更多信息,请参见将来的测试宏。

使用gcc时,您还可以执行gcc -std=gnu11或其他-std=gnu*-std=c11-std=gnu11相同,只是宏_GNU_SOURCE是在编译时预定义的。

根据 Linux 手册页的 fdopen(),Linux/glibc 需要定义_POSIX_C_SOURCE宏:

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

   fdopen(): _POSIX_C_SOURCE

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

相关内容

  • 没有找到相关文章

最新更新