6

わかりました、最初に、公正な警告、これはクラスのプロジェクトです。メモリリークを修正する以外に、助けを求めていません。私は、この C コードの領域で恐ろしいコーディング慣行に従ってきたと思います。いずれにせよ、Valgrind を実行してメモリ リークが発生している場所を検索すると、どのメモリ リークが見逃されているのかがまったくわかりません。解放していない少なくとも 2 つの文字列があることはわかっていますが、純粋に valgrind 出力のサイズに基づいて malloc() しました。プロジェクトから不要なコードをいくつか取り除いたので、valgrind の行番号がずれている可能性があります。そのため、便宜上、コメントを付けました。

少し背景として、シェルの基本的な機能を書いています。現在、次のことを行っています。

1.ユーザー入力を取得する

2. 入力を解析して cmdin 構造体にする

3. パイプが含まれていない場合は、コマンドを実行します。

4.作成した cmdin からスペースを解放し、手順 1 からやり直します。ここで問題が発生します。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_SIZE 1024
#define MAX_CLEN 2097152
#define READ_END 0
#define WRITE_END 1
#define CHILD_STATUS 0
struct cmdin
{
    char *cmd;
    char **args;
    int nargs;
    int pipeflag;
};

//check if the last argument of the command passed to it is a pipe.
//if so, return the position of the pipe, otherwise, return 0
//if it has already found a pipe, it will return the position of that pipe
int conpipe(struct cmdin * cmd)
{
    if(!cmd->pipeflag)
    {
        if(!strcmp(cmd->args[cmd->nargs], "|"))
        {
            return cmd->nargs;
        }
        else
        {
            return 0;
            /* PROBLEM LINE BELOW */
        }  // line 46, where valgrind claims one of the problems exists
    }
    else
    {
        //printf("pipeflag: %d\n", cmd->pipeflag);
        return (cmd->pipeflag);
    }
}

//free the command after each runthrough
int freeze(struct cmdin cmd)
{
    int i;
    for(i=0; i <= (cmd.nargs); i++)
    {
        //printf("cmd.args[%d]: %s\n",i, cmd.args[i]);
        /* PROBLEM LINE BELOW*/
        free(cmd.args[i]); //this is line 62, as noted by valgrind
    }
    free(cmd.args);
    free(cmd.cmd);
    return 0;
}

//parse input, and add a null to the end
struct cmdin * parse(char *cmd)
{
    //allocate space for the command 
    struct cmdin *ped = malloc(sizeof(struct cmdin)); 
    //declare pipeflag, and nargs as 0
    ped->pipeflag = 0;
    ped->nargs = 0;
    //allocate space for the array of strings, and for the string cmd.
    ped->args =  malloc(sizeof(char*) * MAX_SIZE);
    ped->cmd = malloc(sizeof(char) * MAX_SIZE);
    //scan the input, and put the first argument into the cmd string
    sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
    //allocate space for the next string, and then copy cmd to it.
    ped->args[ped->nargs] =  malloc(sizeof(char) * MAX_SIZE);
    strcpy(ped->args[ped->nargs], ped->cmd);
    ped->pipeflag = conpipe(ped);

    /* PROBLEM LINE BELOW*/
    ped->nargs++;     // line 86, where valgrind claims the second leak is called?
    ped->args[ped->nargs] =  malloc(sizeof(char) * MAX_SIZE);
    //loop that allocates space for a command, and then assigns
    //the next arg to it.
    while(sscanf(cmd, " %s %[^\n]", ped->args[ped->nargs], cmd) == 2)
    {
        ped->pipeflag = conpipe(ped);
        ped->nargs++;
        ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
    }

    strncpy(ped->args[ped->nargs], cmd, strlen(cmd)-1);
    ped->nargs++;
    //ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
    ped->args[ped->nargs] = NULL;

    return ped;

}

int main()
{
    char cwd[MAX_CLEN];
    getcwd(cwd, sizeof(cwd));
    char input[MAX_CLEN]; 
    char *exit  = "exit\n";
    char *cd = "cd";

    //Command to take input
    printf("tosh$ ");
    fgets(input, sizeof input, stdin);    

    while(strcmp(input, exit))
    {
       //Command to parse input
       struct cmdin *cmd;
       cmd = parse(input);
       //if there is not a pipeflag
       if(!cmd->pipeflag)
       {
            //Change directories
            if(!strcmp(cd, cmd->args[0]))
            {
                chdir(cmd->args[1]); 
                getcwd(cwd, sizeof(cwd));
            }
            else if(strcmp(input, exit))
            {
                //command to run input
                int child_pid;
                child_pid = fork();
                if(child_pid == 0)
                {
                    if(strcmp(cmd->args[1],cmd->args[0]))
                    {
                        execvp(cmd->cmd, cmd->args);
                    }
                    else
                    {
                        free(cmd->args[1]);
                        cmd->args[1] = NULL;
                        cmd->nargs--;
                        execvp(cmd->cmd, cmd->args);

                    }
                }
                else
                {
                    wait(&child_pid);
                }
            }
            freeze(*cmd);
            free(cmd);
        }       
        //Command to take input
        printf("tosh$ ");
        fgets(input, sizeof input, stdin);
    }
    return 0;
}

注: この valgrind の入力は次のようになります。

tosh$ ls -al
tosh$ exit

valgrind のヒープとリークの概要は次のとおりです。

HEAP SUMMARY:
==4901==     in use at exit: 4,096 bytes in 2 blocks
==4901==   total heap usage: 6 allocs, 4 frees, 24,600 bytes allocated
==4901== 
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 1 of 2 
==4901==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901==    by 0x400A48: parse (tosh.c:46)
==4901==    by 0x400C97: main (tosh.c:86)
==4901== 
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 2 of 2
==4901==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901==    by 0x400BDE: parse (tosh.c:62)
==4901==    by 0x400C97: main (tosh.c:86)
==4901== 
==4901== LEAK SUMMARY:
==4901==    definitely lost: 4,096 bytes in 2 blocks
==4901==    indirectly lost: 0 bytes in 0 blocks
==4901==      possibly lost: 0 bytes in 0 blocks
==4901==    still reachable: 0 bytes in 0 blocks
==4901==         suppressed: 0 bytes in 0 blocks

更新: リクエストに応じて。私のメイクファイル:

CC=gcc
CFLAGS=-g -Wall
TARGET=tosh

$(TARGET): $(TARGET).c
    $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c

Valgrind バージョン: valgrind-3.7.0

gcc バージョン: gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2

エコー $LD_PRELOAD:

(これは何も印刷しませんでした)

4

2 に答える 2

4

再現に成功していません。しかし、いくつかのメモ。

  1. 利用可能なエラーと警告を使用してください。

    gcc -Wall -Wextra -pedantic …
    
  2. 戻り値と操作の成功を確認します。これは、バグを減らすために不可欠です。特に、1 マイル先のユーザー ケースでしか明らかにならないバグを減らします。


if (!(foo = malloc(size))) {
    perror("mem");
}

...


/* This line is no good: */
sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
/*      |                           |
 *      +----------+----------------+
 *                 |
 *                 +--- Not good. UB. (Input / Output same.)
 */

カウントとサイズを取得するルーチンを追加できます...

i = sscanf(cmd, "%s%n %[^\n]%n", ped->cmd, &n1, cmd, &n2);

if (i < 2) {
    /* Debug print. */
    fprintf(stderr, 
            "%s:%-3d; "
            "sscanf => %d items and %d bytes str[%d]<%s>\n", 
            __FILE__, __LINENO_,
            i, n2, n1, ped->cmd
    );
}

于 2013-10-31T07:29:43.803 に答える
3

さて、私は問題が何であるかを理解しました。コメンテーターのほとんどは正しかった。異なるソース コードで valgrind を実行していたということでした。tosh のコピーを ~/usr/bin ($PATH にあります) に保存しています。そのコードを最新の変更 (メモリ リークの問題を修正したものを含む) で更新するのを忘れていました。

valgrind を実行したとき、tosh.c tosh を含むディレクトリから次のコマンドを使用しました。

$ valgrind tosh

私が実行すべきだったのは:

$ valgrind ./tosh

コマンドを更新したところ、コードはメモリ リークなしで正常に実行されました。お世話になりました!申し訳ありませんが、これを理解するのにとても時間がかかりました。

于 2013-10-31T06:56:05.077 に答える