exec () 함수와 그 계열을 설명 해주세요.
exec()
기능과 그 제품군 은 무엇입니까 ? 이 기능은 왜 사용되며 어떻게 작동합니까?
누구든지 이러한 기능을 설명하십시오.
간단히 말해서 UNIX에서는 프로세스와 프로그램의 개념이 있습니다. 프로세스는 프로그램이 실행되는 것입니다.
UNIX "실행 모델"뒤에있는 간단한 아이디어는 수행 할 수있는 두 가지 작업이 있다는 것입니다.
첫 번째는 fork()
상태를 포함하여 현재 프로그램의 복제본을 포함하는 새로운 프로세스를 만드는 to 입니다. 프로세스 간에는 부모와 자식을 파악할 수있는 몇 가지 차이점이 있습니다.
두 번째는 ~로 exec()
, 현재 프로세스의 프로그램을 새로운 프로그램으로 대체합니다.
이 두 가지 간단한 작업을 통해 전체 UNIX 실행 모델을 구성 할 수 있습니다.
위에 더 자세한 정보를 추가하려면 :
새로운 프로세스를 시작하는 매우 간단한 방법을 제공한다는 점에서 UNIX 의 사용 fork()
및 exec()
모범 사례입니다.
이 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 구현에는 COW (기록 fork()
중 복사)라고하는 것을 사용 하는 최적화 기능 이 있습니다. 이것은 fork()
프로그램이 해당 공간에서 무언가를 변경하려고 할 때까지 프로세스 공간의 복사를 지연시키는 트릭 입니다. 이것은 전체 프로세스 공간을 복사 할 필요가 없다는 점에서 사용 fork()
하지 않고 사용하는 프로그램에 유용 exec()
합니다. Linux에서는 fork()
페이지 테이블과 새 작업 구조의 복사본 만 만들어 exec()
두 프로세스의 메모리를 "분리"하는 번거로운 작업을 수행합니다.
이 경우 exec
됩니다 다음 호출 fork
(이 대부분 일 것입니다), 그 프로세스 공간에 쓰기를 발생시키고 그 다음 자식 프로세스를 위해 복사됩니다.
Linux에는 또한 두 프로세스간에 vfork()
거의 모든 것을 공유하는 훨씬 더 최적화 된 . 그 때문에, 아이를 호출 할 때까지 어떤 아이가 할 수있는 일에 제한하고, 부모가 정지있다 exec()
거나 _exit()
.
두 프로세스가 동일한 스택을 공유하기 때문에 부모를 중지해야합니다 (그리고 자식은 현재 함수에서 반환 할 수 없습니다). 이것은 fork()
바로 뒤에 오는 클래식 사용 사례에 대해 약간 더 효율적입니다 exec()
.
전체 exec
호출 제품군 ( execl
, execle
등 execve
)이 있지만 exec
여기서 문맥 상 해당 호출을 의미합니다.
다음 다이어그램 은 명령 으로 디렉토리를 나열하는 데 쉘이 사용되는 일반적인 fork/exec
작업을 보여줍니다 .bash
ls
+--------+
| 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
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 []);
For all of them the initial argument is the name of a file that is to be executed.
For more information read exec(3) man page:
man 3 exec # if you are running a UNIX system
The exec
family of functions make your process execute a different program, replacing the old program it was running. I.e., if you call
execl("/bin/ls", "ls", NULL);
then the ls
program is executed with the process id, current working dir and user/group (access rights) of the process that called execl
. Afterwards, the original program is not running anymore.
To start a new process, the fork
system call is used. To execute a program without replacing the original, you need to fork
, then exec
.
exec
is often used in conjunction with fork
, which I saw that you also asked about, so I will discuss this with that in mind.
exec
turns the current process into another program. If you ever watched Doctor Who, then this is like when he regenerates -- his old body is replaced with a new body.
The way that this happens with your program and exec
is that a lot of the resources that the OS kernel checks to see if the file you are passing to exec
as the program argument (first argument) is executable by the current user (user id of the process making the exec
call) and if so it replaces the virtual memory mapping of the current process with a virtual memory the new process and copies the argv
and envp
data that were passed in the exec
call into an area of this new virtual memory map. Several other things may also happen here, but the files that were open for the program that called exec
will still be open for the new program and they will share the same process ID, but the program that called exec
will cease (unless exec failed).
The reason that this is done this way is that by separating running a new program into two steps like this you can do some things between the two steps. The most common thing to do is to make sure that the new program has certain files opened as certain file descriptors. (remember here that file descriptors are not the same as FILE *
, but are int
values that the kernel knows about). Doing this you can:
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 */
This accomplishes running:
/bin/echo "hello world" > ./output_file.txt
from the command shell.
what is the exec function and its family.
The exec
function family is all functions used to execute a file, such as execl
, execlp
, execle
, execv
, and execvp
.They are all frontends for execve
and provide different methods of calling it.
why is this function used
Exec functions are used when you want to execute (launch) a file (program).
and how does it work.
They work by overwriting the current process image with the one that you launched. They replace (by ending) the currently running process (the one that called the exec command) with the new process that has launched.
For more details: see this link.
The exec(3,3p)
functions replace the current process with another. That is, the current process stops, and another runs instead, taking over some of the resources the original program had.
When a process uses fork(), it creates a duplicate copy of itself and this duplicates becomes the child of the process. The fork() is implemented using clone() system call in linux which returns twice from kernel.
- A non-zero value(Process ID of child) is returned to the parent.
- A value of zero is returned to the child.
- In case the child is not created successfully due to any issues like low memory, -1 is returned to the fork().
Let’s understand this with an example:
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");
In the example, we have assumed that exec() is not used inside the child process.
But a parent and child differs in some of the PCB(process control block) attributes. These are:
- PID - Both child and parent have a different Process ID.
- Pending Signals - The child doesn’t inherit Parent’s pending signals. It will be empty for the child process when created.
- Memory Locks - The child doesn’t inherit its parent’s memory locks. Memory locks are locks which can be used to lock a memory area and then this memory area cannot be swapped to disk.
- Record Locks - The child doesn’t inherit its parent’s record locks. Record locks are associated with a file block or an entire file.
- Process resource utilisation and CPU time consumed is set to zero for the child.
- The child also doesn’t inherit timers from the parent.
But what about the child memory? Is a new address space created for a child?
The answers in no. After the fork(), both parent and child share the memory address space of parent. In linux, these address space are divided into multiple pages. Only when the child writes to one of the parent memory pages, a duplicate of that page is created for the child. This is also known as copy on write(Copy parent pages only when the child writes to it).
Let’s understand copy on write with an example.
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;
}
But why is copy on write necessary?
A typical process creation takes place through fork()-exec() combination. Let’s first understand what exec() does.
Exec() group of functions replaces the child’s address space with a new program. Once exec() is called within a child, a separate address space will be created for the child which is totally different from the parent’s one.
If there was no copy on write mechanism associated with fork(), duplicate pages would have created for the child and all the data would have been copied to child’s pages. Allocating new memory and copying data is a very expensive process(takes processor’s time and other system resources). We also know that in most cases, the child is going to call exec() and that would replace the child’s memory with a new program. So the first copy which we did would have been a waste if copy on write was not there.
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.
Why does parent waits for a child process?
- The parent can assign a task to it’s child and wait till it completes it’s task. Then it can carry some other work.
- Once the child terminates, all the resources associated with child are freed except for the process control block. Now, the child is in zombie state. Using wait(), parent can inquire about the status of child and then ask the kernel to free the PCB. In case parent doesn’t uses wait, the child will remain in the zombie state.
Why is exec() system call necessary?
It’s not necessary to use exec() with fork(). If the code that the child will execute is within the program associated with parent, exec() is not needed.
But think of cases when the child has to run multiple programs. Let’s take the example of shell program. It supports multiple commands like find, mv, cp, date etc. Will be it right to include program code associated with these commands in one program or have child load these programs into the memory when required?
It all depends on your use case. You have a web server which given an input x that returns the 2^x to the clients. For each request, the web server creates a new child and asks it to compute. Will you write a separate program to calculate this and use exec()? Or you will just write computation code inside the parent program?
Usually, a process creation involves a combination of fork(), exec(), wait() and exit() calls.
참고URL : https://stackoverflow.com/questions/4204915/please-explain-the-exec-function-and-its-family
'Programing' 카테고리의 다른 글
Vim 들여 쓰기 xml 파일 (0) | 2020.09.10 |
---|---|
문자열을 정수로 변환 (0) | 2020.09.10 |
iOS 10은 NSLogs를 인쇄하지 않습니다. (0) | 2020.09.10 |
키보드가 표시되고 숨겨지는시기를 감지하는 방법 (0) | 2020.09.10 |
접두사 / 접미사로 이름을 바꾸는 방법? (0) | 2020.09.10 |