exec

exec
Liux 의 bash 셸 에서 외부 명령 을 실행 할 때 이 셸 은 하위 프로 세 스 를 fork 합 니 다. 이 하위 프로 세 스 는 exec 를 디스크 에서 외부 명령 에 대응 하 는 프로그램 을 불 러 옵 니 다.이것 은 exec 의 응용 프로그램 이다.그것 의 응용 을 통 해 원 리 를 보다.
exec 는 실행 가능 한 파일 의 절대 경 로 를 매개 변수 로 하고 현재 실행 중인 사용자 프로 세 스 의 프로 세 스 체 를 실행 가능 한 파일 의 프로 세 스 체 로 교체 하여 새 프로 세 스 의 실행 을 실현 합 니 다. 프로 세 스 의 pid 는 변 하지 않 습 니 다.exec 는 새로운 프로 세 스 를 만 들 지 않 고 원래 프로 세 스 컨 텍스트 의 내용 만 바 꾸 었 다 고 볼 수 있 습 니 다.원래 프로 세 스 의 코드 세그먼트, 데이터 세그먼트, 스 택 세그먼트 가 새로운 프로 세 스 로 대 체 됩 니 다.
실현 상 으로 는 새로운 프로 세 스 를 불 러 온 다음 현재 프로 세 스 의 스 택 환경 을 바 꾸 면 됩 니 다. 다음은 구현 코드 입 니 다.
/*  path            */
int32_t sys_execv(const char *path, const char *argv[])
{
    uint32_t argc = 0;
    while (argv[argc])
    {
        argc++;
    }
    int32_t entry_point = load(path);
    if (entry_point == -1)
    { //         -1
        return -1;
    }

    task_struct *cur = running_thread();
    /*       */
    memcpy(cur->name, path, TASK_NAME_LEN);
    cur->name[TASK_NAME_LEN - 1] = 0;

    intr_stack *intr_0_stack = (intr_stack *)((uint32_t)cur + PG_SIZE - sizeof(intr_stack));
    /*           */
    intr_0_stack->ebx = (int32_t)argv;
    intr_0_stack->ecx = argc;
    intr_0_stack->eip = (void *)entry_point;
    /*                     */
    intr_0_stack->esp = (void *)0xc0000000;

    /* exec   fork,          ,        */
    asm volatile("movl %0, %%esp; jmp intr_exit"
                 :
                 : "g"(intr_0_stack)
                 : "memory");
    return 0;
}

셸 이 외부 명령 을 지원 하려 면 현재 셸 을 개선 해 야 합 니 다. 외부 명령 을 만 났 을 때 fork 키 프로 세 스 가 이 하위 프로 세 스 를 exec 로 호출 하면 외부 프로 세 스 가 실 행 됩 니 다.
int32_t pid = fork();
if (pid)
{
     while(1);
}
else
{
    make_clear_abs_path(argv[0], final_path);
    argv[0] = final_path;

    struct stat file_stat;
    memset(&file_stat, 0, sizeof(struct stat));
    if (stat(argv[0], &file_stat) != -1)
    {
        execv(argv[0], argv);
    }
    while(1);
}

여기 while 두 개 를 살짝 설명 하 겠 습 니 다.
첫 번 째 while (1), 즉 현재 셸 프로 세 스 입 니 다. 이 셸 자 체 는 죽은 순환 입 니 다. 입력 을 계속 감지 하 는 상태 입 니 다. fork 키 프로 세 스 가 있 으 면 현재 프로 세 스 의 스 택 데 이 터 를 수정 하지 않 기 위해 이 곳 에서 공전 합 니 다. 부모 프로 세 스 가 먼저 하위 프로 세 스 와 실 행 됩 니 다. 다시 실행 하면 스 택 의 데 이 터 를 수정 합 니 다.예 를 들 어 하위 프로 세 스 에서 사용 할 finalpath.
두 번 째 while (1), 즉 하위 프로 세 스 가 부모 프로 세 스 의 데 이 터 를 복사 하면 실 행 될 코드 입 니 다. 현재 프로 세 스 의 종료 가 이 루어 지지 않 았 기 때문에 이 프로 세 스 가 함부로 실행 되 지 않도록 종료 해 야 할 위치 에서 공전 합 니 다.나중에 wait 와 exit 가 실 현 된 후에 교체 할 수 있 습 니 다.
현재 셸 은 외부 명령 을 지원 할 수 있 습 니 다. 프로 세 스 테스트 효 과 를 마음대로 작성 하 십시오.
제 Liux 에 prog 프로그램 을 썼 습 니 다. 그 역할 은 한 마디 를 출력 하 는 것 입 니 다. gcc 로 컴 파일 하여 실행 가능 한 파일 로 연결 한 후에 가상 컴퓨터 의 파일 시스템 에 기록 하 는 것 입 니 다. 그림 에서 실행 효과 입 니 다.물론 이 링크 의 과정 은 현재 이 kernel 에서 지원 하 는 대상 파일 을 사용 해 야 합 니 다. 예 를 들 어 printf 함수, Liux 에서 자체 적 으로 가 져 온 printf 가 아 닌 우리 가 실현 하 는 printf 를 사용 해 야 합 니 다.그래서 사용자 프로 세 스 가 실현 할 수 있 는 기능 은 한계 가 있 습 니 다.
매개 변 수 를 지원 하 는 사용자 프로 세 스
이전의 내장 명령 에서 전달 파 라미 터 는 매우 간단 하 다. 호출 할 때 파 라미 터 를 직접 전달 하면 된다. 내장 명령 은 하나의 함수 일 뿐 이 고 창 고 를 통 해 파 라미 터 를 전달 할 수 있다.그러나 외부 명령 에 있어 서 어떻게 "cat file"과 같은 명령 을 지원 할 수 있 습 니까? 이 file 은 앞에서 말 한 매개 변수 입 니 다.
파 라 메 터 를 가 져 오 는 것 은 명령 이 실행 되 기 전에 가 져 온 파 라 메 터 를 명령 에 전달 하려 면 이 명령 에 속 하 는 프로 세 스 가 먼저 스 택 이 있어 야 합 니 다. 그러나 외부 명령 의 실행 은 실질 적 으로 사용자 프로 세 스 를 불 러 오 는 과정 입 니 다. 프로 세 스 는 만 들 지 않 았 습 니 다. 하물며 프로 세 스 의 스 택 입 니 다.
main 함수 의 이런 형식 을 보 셨 을 거 예요.
int main(int argc, char **argv)
{
    return 0;
}

main 함수 에 인자 가 있 습 니 다. 그러면 이 인 자 는 누가 그 에 게 전 달 했 습 니까? 그 에 게 인자 가 있 는 이상 호출 되 어 실 행 된 것 이 분명 합 니 다. 호출 자 는 인 자 를 그 에 게 전 달 했 습 니 다.이 호출 자 는 CRT (c 실행 라 이브 러 리) 입 니 다. CRT 의 가장 중요 한 작업 은 실행 환경 을 초기 화 하 는 것 입 니 다. main 함수 에 들 어가 기 전에 사용자 프로 세 스 를 위 한 조건, 파라미터 등 을 준비 하 는 것 입 니 다.main 함수 가 실 행 된 후에 사용자 프로 세 스 의 자원 을 회수 합 니 다.
우리 가 컴 파일 한 프로그램의 주 체 는 대략 이렇게 생 겼 다.
main 함 수 는 중간 부분 에 끼어 있 을 뿐 시작 도 끝 도 아 닙 니 다.
물론 여기 서 CRT 를 실현 할 수 없습니다. 우리 가 해 야 할 일 은 매개 변 수 를 push 로 들 어 오 는 것 입 니 다. call main 이면 됩 니 다.
그래서 이 허름 한 CRT 는 8 줄 코드 밖 에 없어 요.
[bits 32]
extern main
section .text
global _start
    ;     exec         
    push ebx    ;  argv
    push ecx    ;  argc
    call main

이것 이 야 말로 프로그램의 진정한 입구 입 니 다. 진정한 입구 가 여기에 있 는 이상 main 함수 의 함수 이름 은 아무것도 중요 하지 않 습 니 다. 우 리 는 자신 이 지 으 려 는 임의의 이름 을 사용 할 수 있 습 니 다. 단지 습관 적 으로 main 을 입구 로 사용 할 수 있 습 니 다.
다음은 파 라 메 터 를 가 진 버 전 을 테스트 하여 파 라 메 터 를 인쇄 합 니 다.
cat 명령
앞에서 그렇게 많은 프로 세 스 의 개념 을 말 했 지만 아직 까지 진정 으로 사용 할 수 있 는 프로 세 스 를 실현 하지 못 했다.다음은 cat 명령 을 실행 합 니 다. 현 재 는 간단 한 것 입 니 다. 이 명령 을 통 해 일반 파일 의 데 이 터 를 봅 니 다.
int main(int argc, char **argv)
{
    int buf_size = 1024;
    char abs_path[512] = {0};
    void *buf = malloc(buf_size);
    if (buf == NULL)
    {
        return -1;
    }

    if (argv[1][0] != '/')
    {
        getcwd(abs_path, 512);
        strcat(abs_path, "/");
        strcat(abs_path, argv[1]);
    }
    else
    {
        strcpy(abs_path, argv[1]);
    }

    int fd = open(abs_path, O_RDONLY);
    if (fd != -1)
    {
        int read_bytes = 0;
        while (true)
        {
            read_bytes = read(fd, buf ,buf_size);
            if (read_bytes == -1)
            {
                break;
            }
            write(std_out, buf, read_bytes);
        }
        free(buf);
        close(fd);
    }
    return 0;
}

이것 이 바로 cat 의 실현 이다.다음은 그 기능 을 테스트 해 보도 록 하 겠 습 니 다.
프로 세 스 의 기본 기능 이 있 습 니 다. 디스크 에서 사용자 프로 세 스 를 불 러 올 수 있 고 셸 을 통 해 외부 명령 을 수행 할 수 있 습 니 다.다음 에는 프로 세 스 동기 화 방식 과 프로 세 스 간 통신 방식 이 실 현 됩 니 다.

좋은 웹페이지 즐겨찾기