무자비한 태클부탁드립니다... shell입니다...segmentation faul

yanggak의 이미지

#include <stdio.h>
#include <string.h>
//#include "minishell.h"
#include <unistd.h>

#define LINE_LEN  80
#define MAX_ARGS  64
#define MAX_ARG_LEN  16
#define MAX_PATHS  64
#define MAX_PATH_LEN  96
#define WHITESPACE " .,\t\n"
#define YG ":"

struct command_t {
        char *name;
        int argc;
        char *argv[MAX_ARGS];
};

char *lookupPath(char **, char **);
int parseCommand(char *cLine, struct command_t *cmd);
int parsePath(char **);
void printPrompt();
void readCommand(char *);

int main()
{
        char *pathv[MAX_PATHS]; // pathv는 각 패스들의 문자열을 가리키는 포인터 배열로 이루어져 있다. 즉 pathv[0]는 첫번째 비교할 패스를 가리킴.
        struct command_t command;
        int pid, status;
        char *commandLine;

        commandLine = (char *)malloc(LINE_LEN+1);
        memset(commandLine, 0, LINE_LEN+1);
        if(commandLine == (char *)0)
        {
                printf("ERROR");
        }

        parsePath(pathv);

        while(1) //특정 명령어를 입력하면 shell이 종료될수 있도록 코드를 추가시키자.
        {
                printPrompt();

                fflush(stdin);
                scanf("%s", commandLine);
                //readCommand(commandLine);

                parseCommand(commandLine, &command);

                command.argv[command.argc] = NULL;

                command.name = lookupPath(command.argv , pathv);
                if(command.name == NULL) // 알맞은 패스를 못찾았을 때. 즉 lookupPath함수가 NULL값을 반환했다는 의미이다.
                {
                        printf("\n패스에 맞는 명령어가 없습니다.\n");
                        continue;
                }

                if( (pid = fork()) == 0)
                {
                        execv(command.name, command.argv);
                }

                wait(&status);
        }

        printf("Shell termination.......");
}

int parsePath(char *dirs[]) // PATH라는 환경변수의 값들을 검사하여 :로 구분지어서 각각의 배열에 패스를 입력한다.
{
        char *pathEnvVar;
        char *thePath;
        int i;

        for(i=0 ; i<MAX_ARGS ; i++)
                dirs[i] = NULL;
        pathEnvVar = (char *)getenv("PATH");
        thePath = (char *)malloc(strlen(pathEnvVar) + 1);
        strcpy(thePath, pathEnvVar);

        i=0;

        dirs[i] = (char *)malloc(MAX_PATH_LEN); //패스의 최대 길이가 MAX_PATH_LEN으로 정의해 놨으므로 하나의 패스는 이범위를 벗어날수 없다.
        while( ((dirs[i] = strsep(&thePath, YG)) != NULL) && i<MAX_PATHS) // : 구분자로 path들을 구분하여 각각의 패스를 dir에 저장한다.
                                                                    // MAX_PATH값을 검사하는 이유는 모든 패스들의 개수가 이를 넘으면 안되기 때문이다.
        {
                dirs[++i] = (char *)malloc(MAX_PATH_LEN);
        }

        return i; // 패스들의 개수를 리턴한다. 이렇게 개수를 리턴하게 되면 굳이 lookupPath함수에서 i를 필요이상으로 검사하지 않아도 된다.
}

char *lookupPath(char **argv, char **dir) //MAX_PATHS대신에 parsePath에서 리턴된 값을 사용하여 이를 이용한다. 쓸데없는 비교를 줄이기 위해.
{ // 명령어가 패스안에 있는지 그것의 경로를 찾고 그 패스+명령어 이름을 붙여서 넘겨준다. 절대경로로 입력하면 그 패스를 넘겨준다.
        char *result;
        char *temp;
        int i;
        char pName[MAX_PATH_LEN];

        if(*argv[0] == '/')
        {
                if(access(argv[0], F_OK) == 0)
                {
                        return argv[0];
                }
                //이 값자체를
                //패스가 절대경로로 표현되고 있다는 뜻이다.
                //이 값자체를 리턴하여 child processor에서 execv로 하여 실행시키자.
        }

        for(i=0 ; i<MAX_PATHS ; i++) //access함수를 사용하기 위해선 경로에 filename을 붙여서 검사해야 한다.
        {
                result = (char *)malloc(MAX_PATH_LEN);
                temp = strcat(dir[i], "/");
                result = strcat(dir[i], argv[0]);

                printf("%s", result);

                if(access(result, F_OK) == 0)
                {
                        return result;
                }               //패스들의 종류를 가지고 우리가 찾는 파일이 패스들 안에 있는지 찾아본다.
                                //access()함수의 사용성을 알아보라.
        }

        fprintf(stderr, "%s : command not found\n", argv[0]);
        return NULL;
}

void printPrompt()
{
        //char *promptString;
        //promptString = "Y.G>>"; //프롬프트를 출력하는 곳이다. 이부분을 좀더 정교하게 바꿔보자. 예를들어 현재의 디렉토리를 나타내게..
        printf("Y.G>>");
}
/*
void readCommand(char *commandLine)
{
        fflush(stdin);
        fgets(commandLine, LINE_LEN, stdin); //프롬프트에서 유저가 입력한 명령어들과 옵션이다.
}
*/
int parseCommand(char *cLine, struct command_t *cmd)
{
        int argc;
        char **clPtr;   // 유저가 위에서 입력한 명령어들과 옵션을 각각 나눈다.여기서 맨처음값은 우리가 패스에서 찾고자 하는 파일이름이므로
                        // 맨처음값은 따로 저장을 하는 것이다. 이 함수의 마지막 부분이 이과정에 속한다.
        clPtr = &cLine;
        argc = 0;
        cmd->argv[argc] = (char *)malloc(MAX_ARG_LEN); //처음 아규먼트의 값의 메모리를 할당한다.
        //printf("aa");
        while( (cmd->argv[argc] = strsep(clPtr, WHITESPACE)) != NULL) // WHITESPACE를 구분자로 하여서 문자열을 나눈다. 이는 헤더파일에 명시됨.
        {
                cmd->argv[++argc] = (char *) malloc(MAX_ARG_LEN);
        }

        cmd->argc = argc-1; //명령어에 입력된 아규먼트들의 개수이다.
        cmd->name = (char *)malloc(sizeof(cmd->argv[0])); //따로 저장하는 이유는 execv를 실행할때 경로와 모든 아규먼트값을 넘겨줘야할는데
                                                          //cmd->name을 경로도 포함하여 저장하려는 이유이다. 이는 위의 lookupPath에서 구현된다.
        strcpy(cmd->name, cmd->argv[0]); //찾고자 하는 파일이름이다.
        return 1;
}

제가 짠 shell입니다 태클 부탁드립니다...
아직 실행은 안되네요....
segmentation fault땜에...
이넘을 어떻게 해야될 것 같은데...좋은 방법이 없을까요?

맹고이의 이미지

디버거로 돌려보세요...

yanggak의 이미지

linux에서는 디버거 돌리려면 어떻게 해야하나요?
초보라...너무...쉬운 질문을...
ㅠ.ㅠ 프로그램 구조상의 문제는 없을까요?

If you can dream it,
you can do it

advanced의 이미지

gdb 라고 아주 훌륭한 디버거가 있습니다

KLDPWiki 에서 찾아보시면 좋은 문서 있습니다

한번 살펴보세요 :D

- advanced -

moonzoo의 이미지

segmentation fault 는 대부분 허용된 메모리 영역을 벗어나는 곳에

write할때 발생하더군요.

메모리를 할당하지 않은 곳에 write했다거나

보통 배열로 잡은 영역을 벗어날때도 마니 발생하죠.

간단히 예를 들면 위의 소스에서.

while( ((dirs[i] = strsep(&thePath, YG)) != NULL) && i<MAX_PATHS)
{ 
   dirs[++i] = (char *)malloc(MAX_PATH_LEN); 
} 

while문의 i < MAX_PATHS 조건에서도 i가 MAX_PATHS-1 일때는 while문의 조건이 참이 될텐데
밑에 내려와서 ++i 가 되므로 dirs[MAX_PATHS]이 되어서

segmentation fault가 일어날 수 있습니다.

이런 점에 유의하면서 다시 한번 찾아보시길 --;

yanggak의 이미지

감사합니다. 답변...지금 gdb를 돌려봤는데...
run을 하고 bt를 하니깐 이런 메시지가 뜨더군요...
(gdb) run
Starting program: /home/os2004spring/CIM301-01/st20013133/exercise/shellex
Y.G>>ls

Program received signal SIGSEGV, Segmentation fault.
0x080488c9 in lookupPath ()
(gdb) bt
#0 0x080488c9 in lookupPath ()
#1 0x08048758 in main ()
#2 0x420158d4 in __libc_start_main () from /lib/i686/libc.so.6
(gdb)

이렇게 나오면 lookuPath에서 문제가 있다는 뜻인가요? 뭔가 잘 이해가 안되네요...ㅠ.ㅠ

If you can dream it,
you can do it

cdpark의 이미지

네. loouPath를 돌리다가 죽었다는 뜻입니다. 하지만 꼭 프로그램의 잘못이 loouPath에 있다는 뜻은 아닙니다. 다른 부분에서 잘못 잡은 포인터를 넘겨줘서 무고한(?) lookuPath가 실수한 걸수도 있고, 심하게는 잘못된 malloc/free 쌍이나 stack 영역의 침범 등으로 인해 메모리가 꼬여서 어쩌다가 lookuPath에서 문제를 일으킨 걸 수도 있습니다.

처음 컴파일할 때에 debugging 옵션을 넣으면 좀 더 많은 정보를 얻을 수 있습니다.

gcc -g shellex.c -o shellex

더 심한 경우는 디버깅 옵션을 주면 에러가 나는 위치가 바뀔 수도 있고, 나던 에러가 발생하지 않을 수 있습니다. :twisted:

yanggak의 이미지

감사합니다...
질문이 하나 더 있는데요...
위에서 프롬프트를 출력할때
그 컴퓨터의 호스트네임으로 프롬프트를 만들고 싶은데
어떻게 하면 될가요?
호스트네임을 가져오려면 어떻게 하지요?
패스를 가져오는 방법과 같은가요?
이런방법 외에 다른 유연성 있는 방법은 없는지요?
예를 들어 현재의 디렉토리를 가리킨다든지...

If you can dream it,
you can do it

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.