我自己写的一个shell发生的内存泄漏

问题描述 投票:0回答:1

我用C系统编程写了一个shell。这个shell接收连续连接20个管道('|')的注释并将它们解密为子进程和父进程。代码执行命令没有问题,但是当我进行内存泄漏查询时使用 Valgrind,我看到发生了内存泄漏。Valgrind 将此原因显示为 strdup,但即使我在 strdup 之后使用 free,问题也没有解决,内存泄漏继续发生。

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#define MAX_LINE 1024 // The maximum length command
#define MAX_COMMAND 20 // Maximum number of commands in a single line
extern char* strdup(const char*);
void log_child_process(pid_t pid, char *command);

void execute_command(char *command, int in_fd, int out_fd) {
    pid_t pid = fork();

    if (pid == 0) {
        // Child process
        if (in_fd != 0) {
            dup2(in_fd, 0);
            close(in_fd);
        }
        if (out_fd != 1) {
            dup2(out_fd, 1);
            close(out_fd);
        }

        log_child_process(getpid(), command); 
        execl("/bin/sh", "sh", "-c", command, NULL);
        perror("execl");
        exit(1);
    } else if (pid < 0) {
        perror("fork");
        exit(1);
    }
}

void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\nYou can not exit with CTRL+C pressed. ':q' to exit.\n");
        printf("myshell> ");
        fflush(stdout);
    } else {
        printf("\nReceived signal %d, exiting.\n", sig);
        exit(0);
    }
}


void log_child_process(pid_t pid, char *command) {
    time_t rawtime;
    struct tm *timeinfo;
    char filename[80];
    char *file_extension = ".txt";

    time(&rawtime);
    timeinfo = localtime(&rawtime);

    strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S", timeinfo);
    strcat(filename, file_extension);

    FILE *file = fopen(filename, "a");
    if (file == NULL) {
        perror("Error opening log file");
        return;
    }

    fprintf(file, "PID: %d, Command: %s\n", pid, command);
    fclose(file);
}

char *strtrim(char *str) {
    char *end;

    // Trim leading space
    while (isspace(*str)) {
        str++;
    }

    if (*str == 0) {  // All spaces?
        return str;
    }

    // Trim trailing space
    end = str + strlen(str) - 1;
    while (end > str && isspace(*end)) {
        end--;
    }
    *(end + 1) = '\0';

    // Allocate new memory for trimmed string and copy it
    char *new_str = strdup(str);

    return new_str;
}

void split_commands(char *input, char **commands, int *num_commands) {
    int count = 0;
    const char delimiter[] = "|";
    char *token;
   
    // Count the number of commands
    for (int i = 0; input[i] != '\0'; i++) {
        if (input[i] == '|')
            count++;
    }
    *num_commands = count + 1;

    // Split input into commands
    token = strtok(input, delimiter);
    for (int i = 0; i < *num_commands; i++) {
        commands[i] = strtrim(token);
        token = strtok(NULL, delimiter);
    }
}

void handle_redirections(char **command, int *in_fd, int *out_fd) {
    char *input_file = NULL;
    char *output_file = NULL;
    char *token;
    const char delimiter[] = " \t\n";

    char *cmd = strdup(*command);
    char *new_command = (char *) calloc(MAX_LINE, sizeof(char));

    token = strtok(cmd, delimiter);
    while (token != NULL) {
        if (strcmp(token, "<") == 0) {
            token = strtok(NULL, delimiter);
            input_file = strdup(token);
        } else if (strcmp(token, ">") == 0) {
            token = strtok(NULL, delimiter);
            output_file = strdup(token);
        } else {
            strcat(new_command, token);
            strcat(new_command, " ");
        }
        token = strtok(NULL, delimiter);
    }

    *command = strtrim(new_command);

    if (input_file != NULL) {
        *in_fd = open(input_file, O_RDONLY);
        if (*in_fd < 0) {
            perror(input_file);
            exit(1);
        }
        free(input_file);
    }

    if (output_file != NULL) {
        *out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
        if (*out_fd < 0) {
            perror(output_file);
            exit(1);
        }
        free(output_file);
    }

    free(cmd);
    free(new_command);
}

int main(int argc , char *argv[]) {
if(argc > 1) {
    fprintf(stderr, "Usage: gcc -o program_name program_name.c && ./program_name\n");
    exit(EXIT_FAILURE);
}

char input[MAX_LINE];
char *commands[MAX_COMMAND];
int num_commands;

signal(SIGINT, signal_handler);

while (1) {
    printf("myshell> ");
    fflush(stdout);

    if (fgets(input, MAX_LINE, stdin) == NULL) {
        break;
    }
    // Remove trailing newline character
    input[strlen(input) - 1] = '\0';

    if (strcmp(input, ":q") == 0) {
        exit(0);
    }

    split_commands(input, commands, &num_commands);

    if(num_commands > MAX_COMMAND){
        fprintf(stderr, "Error: Single line can have maximum %d commands.\n", MAX_COMMAND);
        exit(EXIT_FAILURE);
    }

    int in_fd = 0;
    int out_fd = 1;
    int fd[2];

    for (int i = 0; i < num_commands; i++) {
        if (i < num_commands - 1) {
            if (pipe(fd) < 0) {
                perror("pipe");
                exit(1);
            }
            out_fd = fd[1];
        } else {
            out_fd = 1;
        }

        handle_redirections(&commands[i], &in_fd, &out_fd);

        execute_command(commands[i], in_fd, out_fd);

        if (in_fd != 0) {
            close(in_fd);
        }
        if (out_fd != 1) {
            close(out_fd);
        }
        in_fd = fd[0];
    }

    for (int i = 0; i < num_commands; i++) {
        free(commands[i]);
        commands[i] = NULL; // added to avoid dangling pointer
    }

    for (int i = 0; i < num_commands; i++) {
        wait(NULL);
    }

}
    /*for (int i = 0; i < num_commands; i++) {
    free(commands[i]);
}*/


return 0;
}

Valgrind 结果如下:

enter image description here

我正在尝试防止代码中的内存泄漏。

c memory-leaks
1个回答
0
投票

丢失的东西是

strtrim
分配的所以应该是
command
.

handle_redirections
写入
*command
而没有释放之前的内容。

© www.soinside.com 2019 - 2024. All rights reserved.