首页 文章

使用无效命令的fork会导致valgrind中的内存泄漏

提问于
浏览
4

我有以下代码在fork中执行无效命令 . 以下代码返回valgrind中的内存泄漏 .

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>


int external_cmd(char **argv)
{
    int pid;

    if ((pid = fork()) == -1)
        return -1;

    if (pid == 0) {
        /* child */
        execvp(argv[0], argv);
        exit(0);

    } else if (pid < 0)
        return -1;

    int status;
    while (wait(&status) != pid);

    return 0;
}

int main ()
{
    char *argv[8] = {0};
    argv[0] = "tawtaw"; //<--------- invalid command
    argv[1] = "-a";

    char *mem = strdup("anychar");

    /* fork call */
    external_cmd(argv);

    free(mem);

   return(0);
}

用valgrind返回执行上面的代码:

$ valgrind --leak-check=full --show-leak-kinds=all ./test
==11573== Memcheck, a memory error detector
==11573== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11573== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11573== Command: ./test
==11573== 
==11574== 
==11574== HEAP SUMMARY:
==11574==     in use at exit: 8 bytes in 1 blocks
==11574==   total heap usage: 1 allocs, 0 frees, 8 bytes allocated
==11574== 
==11574== 8 bytes in 1 blocks are still reachable in loss record 1 of 1
==11574==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11574==    by 0x4EBF729: strdup (strdup.c:42)
==11574==    by 0x400747: main (in /home/mohamed/Desktop/tech/test/test)
==11574== 
==11574== LEAK SUMMARY:
==11574==    definitely lost: 0 bytes in 0 blocks
==11574==    indirectly lost: 0 bytes in 0 blocks
==11574==      possibly lost: 0 bytes in 0 blocks
==11574==    still reachable: 8 bytes in 1 blocks
==11574==         suppressed: 0 bytes in 0 blocks
==11574== 
==11574== For counts of detected and suppressed errors, rerun with: -v
==11574== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==11573== 
==11573== HEAP SUMMARY:
==11573==     in use at exit: 0 bytes in 0 blocks
==11573==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated
==11573== 
==11573== All heap blocks were freed -- no leaks are possible
==11573== 
==11573== For counts of detected and suppressed errors, rerun with: -v
==11573== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

NOTE: 如果我使用有效命令"ls"而不是"tawtaw"执行代码,那么valgring将不会返回内存泄漏 .

我错过了什么?

2 回答

  • 2

    这是预料之中的 . 当 execve() 无法执行命令时,它会将控制权返回给您的代码,而不是退出并且永远不会从 strdup() 释放内存 .

    execve 成功时,将替换整个文件映像,并且没有剩余的内存分配 strdup() .

  • 1

    你不应该关心这种“泄漏”,因为它只是你的过程异常终止 . 当您只需打印错误消息并退出时,不应该小心地释放资源 . 操作系统将释放所有未完成的资源 .

    如果由于某种原因您仍然担心这一点,请尝试用 return -1; 替换 exit(0) . 在实际程序中,您永远不会使用 exit ,而只能使用释放已分配资源的正常返回路径 . 在C中,使用RAII并在需要异常退出时抛出异常 . 这是您的程序转换为RAII风格:

    #include <cstdlib>
    #include <cstdarg>
    #include <cerrno>
    #include <vector>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <stdexcept>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    // This function should be standard in some kind of POSIX C++ library
    void execvp(const std::string& program, const std::vector<std::string>& args)
    {
        std::vector<const char*> real_argv(args.size()+1);
        for (const std::string& s : args)
            real_argv.push_back(s.c_str());
        real_argv.push_back(nullptr);
        // have to use const_cast because of the broken const model of C
        execvp(program.c_str(), const_cast<char**>(real_argv.data()));
        throw std::runtime_error((std::string("Could not execvp ") + args[0]).c_str());
    }
    
    int external_cmd(const std::string& program, const std::vector<std::string>& args)
    {
        int pid;
    
        if ((pid = fork()) == -1)
            return -1;
    
        if (pid == 0) {
            /* child */
            execvp(program, args);
    
        } else if (pid < 0)
            return -1;
    
        int status;
        while (wait(&status) != pid);
    
        return 0;
    }
    
    int main ()
    {
        try {
            std::vector<std::string> args;
            args.push_back("tawtaw");
            args.push_back("-a");
    
            std::string s("12345678"); // RAII
    
            /* fork call */
            external_cmd(args[0], args);
    
            return EXIT_SUCCESS;
        } catch (std::exception& e) {
            std::cerr << e.what() << ". Exiting.\n";
            return EXIT_FAILURE;
        } catch ( ... ) {
            std::cerr << "Unexpected error. OS message is: " << strerror(errno) << ". Exiting.\n";
            return EXIT_FAILURE;
        }
    }
    

相关问题