execve(2)

#include <unistd.h>

int execve(const char *filename, char *const argv[], char *const envp[]);

실행가능한 파일인 filename의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행합니다.  즉, 현재 실행되는 프로그램의 기능은 없어지고 filename 프로그램을 메모리에 loading하여 처음부터 실행합니다. 

 

UNIX / LINUX에서는 프로세스 생성(fork(2))과 실행할 binary 교체(exec계열함수)는 분리되어 있습니다.

 

execve 호출 후에 일어나는 프로세스의 변화입니다.

- signal 설정이 default로 변경됩니다.
- mmap(2)으로 생성 memory mapping이 보존되지 않습니다.
- shared memory 영역에 대한 access가 해제됩니다.
- message queue descriptor가 close됩니다.
- named semaphore가 close됩니다.
- timer가 보존되지 않습니다.
- opendir로 open된 directory stream이 close됩니다.
- memory lock이 보존되지 않습니다.
- atexit(3), on_exit(3)로 등록된 exit handler가 해제됩니다.
- 모든 thread가 사라집니다.
- aio_read(3)/aio_write(3)과 같은 async I/O 동작이 취소됩니다.
- 일반 file descriptor는 close되지 않습니다.

 

 ※ execve : execute 프로그램 by argument를 vector(배열)로, environment(환경변수)도 변경하면서.

 exec계열함수는 execve(2)만 system call함수이며, 나머지 execl(3), execlp(3), execle(3), execv(3), execvp(3), execvpe(3)는 execve(3)의 wrapping한 함수입니다.

 

 exec 뒤에 붙는 글자의 의미는 

 l : argv가 list로 나열된다는 의미입니다. 그 것의 끝은 NULL.
 v : argv가 vector(배열)로 parameter를 하나를 받는다는 의미입니다. 배열의 마지막값은 NULL
 p : 첫번째 파라미터인 명령어/실행파일이 PATH로 지정된 디렉토리에 있다면 
      full path 또는 상대 path로 하지 않아도 된다는 뜻입니다.
 e : 설정할 환경변수를 parameter로 받는다는 의미입니다.

 

파라미터

filename
    - 교체할 실행 파일 / 명령어.
    - filename은 실행가능한 binary이거나 shell이어야 합니다.
    - filename은 path가 설정되어 있는 디렉토리라고 하더라도 
      절대path나 상대path로 정확한 위치를 지정해야 합니다.
argv
    - c언어의 main(int argc, char *argv[])에서 argv와 비슷하며, main함수에는 argc가 있지만 
      execve에는 argc가 없으므로 main의 argv에 마지막 array 다음은 NULL이어야 있는 것과 같습니다.
envp
    - key=value형식의 환경변수 문자열 배열리스트로 마지막은 NULL이어야 합니다.
    - 만약 기 설정된 환경변수를 사용하려면 environ 전역변수를 그냥 사용합니다.

 

RETURN

없음
    - 지정된 프로그램이 정상적으로 실행되었습니다.
    - return값은 이미 지정된 프로그램의 로직으로 실행되기 때문에 받을 수 없습니다.


-1
    - binary 교체가 실패하였으며, 상세한 오류 내용은 errno 전역변수에 설정됩니다.

 E2BIG : argv 또는 envp list가 너무 많습니다.
 EACCES : filename의 디렉토리에 구성에 search권한이 없거나 
             filename 파일이 실행권한이 없거나 파일이 실행할 수 없는 파일입니다.
             또는 파일 시스템이 마운트되지 않았습니다.
 EFAULT : filename이라는 변수가 access할 수 없는 메모리 영역입니다.
 EINVAL : ELF 실행파일이 하나이상의 PT_INTERP segment를 가진 경우
 EIO : I/O 오류가 발생하였습니다.
 EISDIR : filename이 디렉토리입니다.
 ELIBBAD : ELF가 인식할 수 없는 format입니다.
 ELOOP : 너무 많은 symbolic link depth입니다.
 EMFILE : 프로세스가 열 수 있는 최대 갯수의 file을 open하였습니다.
 ENAMETOOLONG : filename의 이름이 너무깁니다.
 ENFILE : 시스템에서 열 수 있는 최대 갯수의 파일이 열렸습니다.
 ENOENT : filename이 존재하지 않는 파일입니다.
 ENOEXEC : filename이 실행할 수 없는 format입니다.
 ENOMEM : kernel이 사용할 수 있는 메모리가 부족합니다.
 ENOTDIR : filename을 구성하는 디렉토리가 디렉토리가 아닙니다.
 EPERM : 파일시스템이 nosuid로 마운트되었으며, user가 superuser가 아니고 
             filename이 set-user-ID, set-group-ID bit가 설정안됨
 ETXTBSY : 다른 프로세스에 의해서 쓰기를 위해 open된 경우

 


활용 예제

 

Sample. sample1.c : ls 명령어로 치환

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

extern char **environ;

int main(int argc, char *argv[])
{
    char **new_argv;
    char command[]  = "ls";
    int  idx;

    new_argv = (char **)malloc(sizeof(char *) * (argc + 1));

    /* 명령어를 ls로 변경 */
    new_argv[0] = command;

    /* command line으로 넘어온 parameter를 그대로 사용 */
    for(idx = 1; idx < argc; idx++) {
        new_argv[idx] = argv[idx];
    }

    /* argc를 execve 파라미터에 전달할 수 없기 때문에 NULL이 파라미터의 끝을 의미함 */
    new_argv[argc] = NULL;
    if(execve("/usr/bin/ls", new_argv, environ) == -1) {
        fprintf(stderr, "프로그램 실행 error: %s\n", strerror(errno));
        return 1;
    }

    /* ls 명령어 binary로 실행로직이 교체되었으므로 이후의 로직은 절대 실행되지 않습니다. */
    printf("이곳이 이제 ls 명령어라 이 라인은 출력이 되지 않습니다.\n");
    return 0;
}

--------------------------------------------------------------
실행방법
sample1 -al <enter>
결과는 ls -al 한 것과 같음.

 


see also: Process 관리 함수 

 

 

 

블로그 이미지

사용자 자연&사람

행복한 개발자 programmer since 1995.

댓글을 달아 주세요