我正在编写一个代码,该代码将计算父进程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
,因此它被截断了。