exec () 함수와 그 계열을 설명 해주세요.


98

exec()기능과 그 제품군 은 무엇입니까 ? 이 기능은 왜 사용되며 어떻게 작동합니까?

누구든지 이러한 기능을 설명하십시오.


4
Stevens를 다시 읽고 이해하지 못하는 것이 무엇인지 명확히하십시오.
vlabrecque

답변:


245

간단히 말해서 UNIX에서는 프로세스와 프로그램의 개념이 있습니다. 프로세스는 프로그램이 실행되는 환경입니다.

UNIX "실행 모델"뒤에있는 간단한 아이디어는 수행 할 수있는 두 가지 작업이 있다는 것입니다.

첫 번째는 fork()상태를 포함하여 현재 프로그램의 중복 (대부분)을 포함하는 새로운 프로세스를 생성하는 to 입니다. 두 프로세스 간에는 부모와 자식을 파악할 수있는 몇 가지 차이점이 있습니다.

두 번째는 ~로 exec(), 현재 프로세스의 프로그램을 새로운 프로그램으로 대체합니다.

이 두 가지 간단한 작업을 통해 전체 UNIX 실행 모델을 구성 할 수 있습니다.


위에 더 자세한 정보를 추가하려면 :

fork()및 사용exec()새로운 프로세스를 시작하는 매우 간단한 방법을 제공한다는 점에서 UNIX 모범 사례입니다.

fork()호출은 거의 모든면에서 동일한 현재 프로세스를 거의 복제합니다 (예를 들어 일부 구현에서는 리소스 제한을 통해 모든 것이 복사되는 것은 아니지만 가능한 한 가까운 복사본을 만드는 것입니다). 하나의 프로세스 호출 fork() 만 해당 호출에서 두 개의 프로세스가 반환됩니다. 이상하게 들리지만 정말 우아합니다.

새 프로세스 (하위라고 함)는 다른 프로세스 ID (PID)를 가져오고 이전 프로세스 (상위)의 PID를 상위 PID (PPID)로 갖습니다.

두 프로세스가 이제 정확히 동일한 코드를 실행하고 있기 때문에 어떤 것이 무엇인지 알 수 있어야합니다. 반환 코드 fork()는이 정보 를 제공합니다. 자식은 0을 얻고 부모는 자식의 PID를 얻습니다 (fork() 실패하면 자식이 생성되고 부모가 오류 코드를받습니다).

이렇게하면 부모는 자식의 PID를 알고 통신하고, 죽이고, 기다릴 수 있습니다 (자식은 항상를 호출하여 부모 프로세스를 찾을 수 있음 getppid()).

exec()호출은 프로세스의 현재 전체 내용을 새 프로그램으로 대체합니다. 프로그램을 현재 프로세스 공간으로로드하고 진입 점에서 실행합니다.

그래서, fork()그리고 exec()종종 현재 프로세스의 자식으로 실행되는 새 프로그램을 얻기 위해 순서대로 사용됩니다. 쉘은 일반적으로 find쉘 포크 와 같은 프로그램을 실행하려고 할 때마다이를 수행 합니다.find 프로그램을 메모리에 모든 명령 줄 인수, 표준 I / O 등을 설정합니다.

그러나 함께 사용할 필요는 없습니다. 예를 들어 프로그램에 부모 및 자식 코드가 모두 포함되어있는 경우 프로그램이 fork()다음없이 호출하는 것은 완벽하게 허용됩니다 exec()(사용자가 수행하는 작업에주의해야하며 각 구현에는 제한이있을 수 있음).

이것은 단순히 TCP 포트에서 수신 대기하고 부모가 수신 상태로 돌아가는 동안 특정 요청을 처리하기 위해 자신의 사본을 포크하는 데몬에 대해 상당히 많이 사용되었습니다. 이 경우 프로그램에는 상위 하위 코드 가 모두 포함 됩니다.

마찬가지로, 그들은이 완료 알고 프로그램은 필요하지 않는 다른 프로그램 실행하려면 fork(), exec()다음 wait()/waitpid()아이에 대한합니다. 을 사용하여 자식을 현재 프로세스 공간에 직접로드 할 수 있습니다 exec().

일부 UNIX 구현에는 fork()COW (copy-on-write)라고하는 것을 사용 하는 최적화 기능 이 있습니다. 이것은 fork()프로그램이 해당 공간에서 무언가를 변경하려고 시도 할 때까지 프로세스 공간의 복사를 지연시키는 트릭 입니다. 이것은 전체 프로세스 공간을 복사 할 필요가 없다는 점에서 사용 fork()하지 않고 사용하는 프로그램에 유용 exec()합니다. Linux에서는 fork()페이지 테이블과 새 작업 구조의 복사본 만 만들어 exec()두 프로세스의 메모리를 "분리"하는 번거로운 작업을 수행합니다.

이 경우 exec 됩니다 다음 호출 fork(이 대부분 일 것입니다), 그 프로세스 공간에 쓰기의 원인 및 수정이 허용되기 전에 다음, 자식 프로세스를 위해 복사됩니다.

Linux에는 또한 두 프로세스간에 vfork()거의 모든 것을 공유하는 훨씬 더 최적화 된 . 그 때문에, 아이를 호출 할 때까지 어떤 아이가 할 수있는 일에 제한하고, 부모가 정지있다 exec()거나 _exit().

두 프로세스가 동일한 스택을 공유하기 때문에 부모를 중지해야합니다 (그리고 자식은 현재 함수에서 반환 할 수 없습니다). 이것은 fork()바로 뒤에 오는 클래식 사용 사례에 대해 약간 더 효율적입니다 exec().

전체 exec호출 제품군 ( execl, execleexecve)이 있지만 exec여기서 문맥 상 해당 호출 중 하나를 의미합니다.

다음 다이어그램 은 명령 으로 디렉토리를 나열하는 데 쉘이 사용되는 일반적인 fork/exec작업을 보여줍니다 .bashls

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V

12
그런 정교한 설명에 감사드립니다 :)
Faizan

2
찾기 프로그램의 쉘 참조에 감사드립니다. 정확히 내가 이해해야하는 것.
사용자

exec현재 프로세스의 IO를 리디렉션하는 데 유틸리티가 사용되는 이유는 무엇 입니까? 인수없이 exec를 실행하는 "null"케이스가이 규칙에 어떻게 사용 되었습니까?
레이

@Ray, 나는 항상 그것을 자연스러운 확장으로 생각했습니다. 당신이 생각한다면 exec수단 후, 서로이 과정에서 현재 프로그램 (쉘) 교체로 하지 캔으로 대체하는 다른 프로그램을 지정하기 만하면 의미 하지 않는다 그것을 대체합니다.
paxdiablo

"자연적인 확장"이 "유기적 성장"의 선을 따라 무언가를 의미한다면 당신이 의미하는 바를 봅니다. 프로그램 교체 기능을 지원하기 위해 리디렉션이 추가 된 것으로 보이며, 프로그램 exec없이 호출되는 퇴화 된 경우에이 동작이 남아있는 것을 볼 수 있습니다 . 실제로 취득하는 프로그램 - 그러나 약간은 새로운 프로그램에 대한 리디렉션의 원래 유용성 이후이 시나리오에서 이상한 executed - 사라지고 현재 프로그램 리디렉션, 유용한 유물이 -되지 않는 executed 또는 시작을 대신에.
Ray

36

exec () 계열의 함수는 다른 동작을합니다.

  • l : 인수가 문자열 목록으로 main ()에 전달됩니다.
  • v : 인수는 문자열 배열로 main ()에 전달됩니다.
  • p : 새 실행 프로그램을 검색 할 경로
  • e : 호출자가 환경을 지정할 수 있습니다.

혼합 할 수 있으므로 다음이 있습니다.

  • int execl (const char * path, const char * arg, ...);
  • int execlp (const char * file, const char * arg, ...);
  • int execle (const char * path, const char * arg, ..., char * const envp []);
  • int execv (const char * path, char * const argv []);
  • int execvp (const char * file, char * const argv []);
  • int execvpe (const char * file, char * const argv [], char * const envp []);

이들 모두에 대해 초기 인수는 실행될 파일의 ​​이름입니다.

자세한 내용은 exec (3) 매뉴얼 페이지를 참조하십시오 .

man 3 exec  # if you are running a UNIX system

1
흥미롭게도 execve()POSIX에 의해 정의 된 목록에서 누락 되었고 POSIX에 execvpe()의해 정의되지 않은 을 추가 했습니다 (주로 역사적 선례의 이유로 인해 기능 집합을 완료합니다). 그렇지 않으면, 패밀리의 명명 규칙에 대한 유용한 설명 — paxdiablo 의 유용한 부속물 인 기능의 작동에 대해 자세히 설명 하는 답변 입니다.
Jonathan Leffler

그리고, 당신의 변호에서, 나는 execvpe()(et al)에 대한 리눅스 man 페이지 가 나열하지 않는다는 것을 알았습니다 execve(); 별도의 매뉴얼 페이지 (최소한 Ubuntu 16.04 LTS에 있음)가 있습니다. 차이점은 다른 exec()제품군 기능은 섹션 3 (기능) execve()에 나열되고 섹션 2 (시스템 호출)에 나열되어 있다는 점입니다. 기본적으로 패밀리의 다른 모든 기능은 execve().
Jonathan Leffler

18

exec기능의 제품군은 프로세스가 실행 된 이전 프로그램을 대체하는 다른 프로그램을 실행합니다. 즉, 전화하면

execl("/bin/ls", "ls", NULL);

그런 다음 ls프로그램은 프로세스 ID, 현재 작업 디렉토리 및 호출 한 프로세스의 사용자 / 그룹 (액세스 권한)으로 실행됩니다.execl 됩니다. 이후에는 원래 프로그램이 더 이상 실행되지 않습니다.

새 프로세스를 시작하기 위해 fork시스템 호출이 사용됩니다. 원본을 바꾸지 않고 프로그램을 실행하려면 fork, exec.


정말 도움이되어 주셔서 감사합니다. 저는 현재 exec () 사용을 요구하는 프로젝트를 진행 중이며 귀하의 설명이 제 이해를 굳혔습니다.
TwilightSparkleTheGeek

7

exec 기능과 그 제품군은 무엇입니까?

exec기능 군은 같은 파일을 실행하기위한 모든 함수 인 execl, execlp, execle, execv, 및 execvp.They 대한 모든 프론트 엔드이다 execve하고 호출하는 다른 방법을 제공한다.

이 기능이 사용되는 이유

Exec 함수는 파일 (프로그램)을 실행 (시작) 할 때 사용됩니다.

어떻게 작동합니까?

현재 프로세스 이미지를 시작한 이미지로 덮어 쓰는 방식으로 작동합니다. 현재 실행중인 프로세스 (exec 명령을 호출 한 프로세스)를 시작된 새 프로세스로 (종료하여) 대체합니다.

자세한 내용 은이 링크를 참조하십시오 .


7

exec와 함께 사용되는 경우가 많으며 fork, 이에 대해 물어 보셨으므로이를 염두에두고 논의하겠습니다.

exec현재 프로세스를 다른 프로그램으로 바꿉니다. 닥터 후를 본 적이 있다면 이것은 그가 재생하는 것과 같습니다. 그의 오래된 몸은 새로운 몸으로 대체됩니다.

프로그램에서 이런 일이 발생하는 방식 은 프로그램 인수 (첫 번째 인수)로 exec전달하는 파일이 exec현재 사용자 (프로세스의 사용자 ID)에 의해 실행 가능한지 확인하기 위해 OS 커널이 확인하는 많은 리소스입니다. 만들기 exec전화) 그리고 만약 그것이 가상 메모리로 새로운 프로세스 및 복사 현재 프로세스의 가상 메모리 매핑을 대체 있도록 argv하고 envp에 전달 된 데이터를 exec새로운 가상 메모리 맵의 영역으로 전화를. 여기에서 몇 가지 다른 일이 발생할 수도 있지만 호출 한 프로그램에 대해 열려 있던 파일 exec은 새 프로그램에 대해 여전히 열려 있고 동일한 프로세스 ID를 공유하지만 호출 한 프로그램 exec은 중지됩니다 (exec가 실패하지 않는 한).

이렇게하는 이유 프로그램 실행을 이와 같이 두 단계로 분리 하여 두 단계 사이에 몇 가지 작업을 수행 할 수 있기 때문입니다. 가장 일반적인 방법은 새 프로그램에 특정 파일이 특정 파일 설명 자로 열려 있는지 확인하는 것입니다. (여기서 파일 디스크립터는와 같지 않지만 커널이 알고있는 값이라는 것을 기억하십시오 ). 이렇게하면 다음을 수행 할 수 있습니다. FILE *int

int X = open("./output_file.txt", O_WRONLY);

pid_t fk = fork();
if (!fk) { /* in child */
    dup2(X, 1); /* fd 1 is standard output,
                   so this makes standard out refer to the same file as X  */
    close(X);

    /* I'm using execl here rather than exec because
       it's easier to type the arguments. */
    execl("/bin/echo", "/bin/echo", "hello world");
    _exit(127); /* should not get here */
} else if (fk == -1) {
    /* An error happened and you should do something about it. */
    perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */

이것은 다음을 실행합니다.

/bin/echo "hello world" > ./output_file.txt

명령 셸에서.


5

프로세스가 fork ()를 사용하면 자신의 복제본을 만들고이 복제본은 프로세스의 자식이됩니다. fork ()는 커널에서 두 번 반환하는 Linux에서 clone () 시스템 호출을 사용하여 구현됩니다.

  • 0이 아닌 값 (하위 프로세스 ID)이 부모에게 반환됩니다.
  • 0 값이 자식에게 반환됩니다.
  • 메모리 부족과 같은 문제로 인해 자식이 성공적으로 생성되지 않은 경우 -1이 fork ()에 반환됩니다.

예를 들어 이것을 이해합시다.

pid = fork(); 
// Both child and parent will now start execution from here.
if(pid < 0) {
    //child was not created successfully
    return 1;
}
else if(pid == 0) {
    // This is the child process
    // Child process code goes here
}
else {
    // Parent process code goes here
}
printf("This is code common to parent and child");

이 예제에서는 자식 프로세스 내에서 exec ()가 사용되지 않는다고 가정했습니다.

그러나 부모와 자식은 PCB (프로세스 제어 블록) 속성 중 일부가 다릅니다. 이것들은:

  1. PID-자식과 부모 모두 프로세스 ID가 다릅니다.
  2. Pending Signals-자식은 부모의 보류 신호를 상속하지 않습니다. 생성 될 때 자식 프로세스에 대해 비어 있습니다.
  3. 메모리 잠금-자식은 부모의 메모리 잠금을 상속하지 않습니다. 메모리 잠금은 메모리 영역을 잠그는 데 사용할 수있는 잠금이며이 메모리 영역은 디스크로 스왑 할 수 없습니다.
  4. 레코드 잠금-자식은 부모의 레코드 잠금을 상속하지 않습니다. 레코드 잠금은 파일 블록 또는 전체 파일과 연관됩니다.
  5. 프로세스 리소스 사용률 및 소비 된 CPU 시간은 하위 항목에 대해 0으로 설정됩니다.
  6. 또한 자식은 부모로부터 타이머를 상속하지 않습니다.

그러나 어린이 기억은 어떻습니까? 자녀를위한 새 주소 공간이 생성됩니까?

아니오의 답변. fork () 다음에 부모와 자식 모두 부모의 메모리 주소 공간을 공유합니다. Linux에서 이러한 주소 공간은 여러 페이지로 나뉩니다. 자식이 부모 메모리 페이지 중 하나에 쓸 때만 해당 페이지의 복제본이 자식에 대해 생성됩니다. 이것은 쓰기시 복사 (하위가 쓸 때만 상위 페이지 복사)라고도합니다.

예를 들어 copy on write를 이해합시다.

int x = 2;
pid = fork();
if(pid == 0) {
    x = 10;
    // child is changing the value of x or writing to a page
    // One of the parent stack page will contain this local               variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page.  
}
else {
    x = 4;
}

하지만 Copy on Write가 필요한 이유는 무엇입니까?

일반적인 프로세스 생성은 fork ()-exec () 조합을 통해 이루어집니다. 먼저 exec ()가 무엇을하는지 이해합시다.

Exec () 함수 그룹은 자식의 주소 공간을 새 프로그램으로 대체합니다. 자식 내에서 exec ()가 호출되면 자식에 대해 부모의 주소 공간과 완전히 다른 별도의 주소 공간이 생성됩니다.

fork ()와 관련된 쓰기 메커니즘이 없으면 중복 페이지가 하위에 대해 생성되고 모든 데이터가 하위 페이지에 복사됩니다. 새 메모리를 할당하고 데이터를 복사하는 것은 매우 비용이 많이 드는 프로세스입니다 (프로세서의 시간과 기타 시스템 리소스가 필요함). 또한 대부분의 경우 자식이 exec ()를 호출하고 자식의 메모리를 새 프로그램으로 대체한다는 것도 알고 있습니다. 그래서 우리가 한 첫 번째 사본은 기록 중 사본이 없으면 낭비되었을 것입니다.

pid = fork();
if(pid == 0) {
    execlp("/bin/ls","ls",NULL);
    printf("will this line be printed"); // Think about it
    // A new memory space will be created for the child and that   memory will contain the "/bin/ls" program(text section), it's stack, data section and heap section
else {
    wait(NULL);
    // parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue
}
return 1; // Both child and parent will exit with status code 1.

부모가 자식 프로세스를 기다리는 이유는 무엇입니까?

  1. 부모는 자식에게 작업을 할당하고 작업이 완료 될 때까지 기다릴 수 있습니다. 그런 다음 다른 작업을 수행 할 수 있습니다.
  2. 자식이 종료되면 프로세스 제어 블록을 제외하고 자식과 관련된 모든 리소스가 해제됩니다. 이제 아이는 좀비 상태입니다. wait ()를 사용하여 parent는 child의 상태를 조회 한 다음 커널에 PCB를 해제하도록 요청할 수 있습니다. 부모가 wait를 사용하지 않는 경우 자식은 좀비 상태로 유지됩니다.

exec () 시스템 호출이 필요한 이유는 무엇입니까?

fork ()와 함께 exec ()를 사용할 필요는 없습니다. 자식이 실행할 코드가 부모와 관련된 프로그램 내에 있으면 exec ()가 필요하지 않습니다.

그러나 자녀가 여러 프로그램을 실행해야하는 경우를 생각해보십시오. 쉘 프로그램의 예를 들어 보겠습니다. find, mv, cp, date 등과 같은 여러 명령을 지원합니다. 이러한 명령과 관련된 프로그램 코드를 하나의 프로그램에 포함하거나 필요할 때 자식이 이러한 프로그램을 메모리에로드하도록하는 것이 옳습니까?

그것은 모두 사용 사례에 달려 있습니다. 클라이언트에 2 ^ x를 반환하는 입력 x를 제공하는 웹 서버가 있습니다. 각 요청에 대해 웹 서버는 새 자식을 만들고 계산하도록 요청합니다. 이것을 계산하고 exec ()를 사용하는 별도의 프로그램을 작성 하시겠습니까? 아니면 부모 프로그램 안에 계산 코드를 작성 하시겠습니까?

일반적으로 프로세스 생성에는 fork (), exec (), wait () 및 exit () 호출의 조합이 포함됩니다.


4

exec(3,3p)기능을 대체 서로 현재의 프로세스를. 즉, 현재 프로세스 가 중지 되고 대신 다른 프로세스 실행되어 원래 프로그램이 보유한 일부 리소스를 인수합니다.


6
좀 빠지는. 현재 프로세스 이미지 를 새 프로세스 이미지로 바꿉니다 . 프로세스는 동일한 pid, 동일한 환경 및 동일한 파일 설명자 테이블을 가진 동일한 프로세스입니다. 변경된 것은 전체 가상 메모리 및 CPU 상태입니다.
JeremyP 2010

@JeremyP "동일한 파일 설명자"가 여기서 중요했습니다. 쉘에서 리디렉션이 어떻게 작동하는지 설명합니다! exec가 모든 것을 덮어 쓰면 리디렉션이 어떻게 작동하는지 의아해했습니다! 감사합니다
FUD
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.