그래서 나는 이것에 대해 잘 이해하고 있다고 생각했지만 테스트를 실행했는데 (내가 누군가와 동의하지 않은 대화에 대한 응답으로) 내 이해에 결함이 있음을 발견했습니다 …
가능한 한 자세하게 쉘에서 파일을 실행할 때 정확히 어떤 일이 발생합니까? 내 말은 ./somefile some arguments
쉘에 입력하고 return 키를 누른 somefile
다음 cwd에 있고에 대한 읽기 + 실행 권한이 somefile
있는 경우 후드에서 어떻게됩니까?
나는 그 대답이 다음과 같다고 생각 했다.
- 쉘
exec
은 경로를 전달하여 syscall을 만듭니다.somefile
- 커널은 파일 의 매직 번호 를 검사
somefile
하고 프로세서가 처리 할 수있는 형식인지 확인합니다. - 매직 번호가 파일이 프로세서가 실행할 수있는 형식임을 나타내는 경우
- 새 프로세스가 작성됩니다 (프로세스 테이블에 항목이 있음).
somefile
메모리에 읽히거나 매핑됩니다. 스택이 생성되고 실행 이 매개 변수의 배열 (a , )somefile
로ARGV
초기화 된 코드의 진입 점으로 이동합니다.char**
["some","arguments"]
- 매직 넘버가 shebang 인 경우
exec()
위와 같이 새 프로세스 를 생성하지만 사용 된 실행 파일은 shebang이 참조하는 인터프리터 (예 :/bin/bash
또는/bin/perl
)somefile
로 전달됩니다.STDIN
- 파일에 유효한 마법 번호가 없으면 “잘못된 파일 (잘못된 마법 번호) : Exec 형식 오류”와 같은 오류가 발생합니다.
그러나 누군가 파일이 일반 텍스트 인 경우 쉘은 명령을 실행하려고한다고 말합니다 (입력 한 것처럼 bash somefile
). 나는 이것을 믿지 않았지만 방금 시도했지만 정확했습니다. 그래서 나는 실제로 여기서 일어나는 일에 대해 약간의 오해를 가지고 있으며 그 역학을 이해하고 싶습니다.
쉘에서 파일을 실행할 때 정확히 어떻게됩니까? (자세한 내용은 합리적입니다 …)
답변
Linux에서 “프로그램 실행 방법”에 대한 결정적인 대답은 LWN.net 에서 놀랍게도 프로그램 실행 방법 및 프로그램 실행 방법 이라는 제목 의 기사입니다 : ELF 바이너리 . 첫 번째 기사는 스크립트를 간략하게 설명합니다. (엄밀히 말하면 결정적인 대답은 소스 코드에 있지만,이 기사는 더 쉽게 읽고 소스 코드에 대한 링크를 제공합니다.)
약간의 실험에 따르면 거의 제대로 된 것으로 나타 났으며 간단한 명령 목록이 포함 된 파일의 실행을 쉘없이 처리해야한다는 것을 보여줍니다. 을 execve (2) 맨 테스트 프로그램의 소스 코드를 포함하고,는 execve; 우리는 이것을 사용하여 쉘없이 어떤 일이 발생하는지 볼 것입니다. 먼저 다음을 testscr1
포함 하는 테스트 스크립트를 작성하십시오.
#!/bin/sh
pstree
그리고 하나만 testscr2
포함하는
pstree
둘 다 실행 가능하게 만들고 둘 다 쉘에서 실행되는지 확인하십시오.
chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less
이제 다음을 사용하여 다시 시도하십시오 execve
(현재 디렉토리에 빌드했다고 가정).
./execve ./testscr1
./execve ./testscr2
testscr1
여전히 실행되지만 testscr2
생산
execve: Exec format error
이것은 쉘이 testscr2
다르게 처리 함을 보여줍니다 . 스크립트 자체를 처리하지는 않지만 여전히 스크립트를 사용 /bin/sh
합니다. 이것은 다음으로 배관 testscr2
하여 확인할 수 있습니다 less
.
./testscr2 | less -ppstree
내 시스템에서 나는
|-gnome-terminal--+-4*[zsh]
| |-zsh-+-less
| | `-sh---pstree
보다시피, 내가 zsh
시작한 less
셸과 시작된 스크립트를 실행하는 데 사용되는 셸 sh
( dash
시스템에 있음)이 pstree
있습니다. 에서 zsh
이 처리됩니다 zexecve
에서 Src/exec.c
: 쉘 사용하는 execve(2)
명령을 실행하려고하고, 실패 할 경우, 그에 따라 (커널도했을 것이다)를 처리, 그것이 오두막이있는 경우 볼 수있는 파일을 읽고, 그 경우 파일 sh
에서 0 바이트를 읽지 않는 한 파일을 실행하려고 시도 하지 않습니다.
for (t0 = 0; t0 != ct; t0++)
if (!execvebuf[t0])
break;
if (t0 == ct) {
argv[-1] = "sh";
winch_unblock();
execve("/bin/sh", argv - 1, newenvp);
}
bash
taliezin이execute_cmd.c
지적한대로 유용한 의견으로 구현 된 동일한 행동 을합니다 .
어딘가에 디스크 파일에 정의 된 간단한 명령을 실행하십시오.
fork ()
- 파이프 연결
- 명령을 찾아
- 방향 전환을하다
execve ()
- 경우
execve
실패한 파일이 실행 모드가 설정되어있는 경우, 참조하십시오. 그렇다면 디렉토리가 아닌 경우 내용을 쉘 스크립트로 실행하십시오.
POSIX는 함수라고 하는 exec(3)
함수 세트를 정의하며이 execve(2)
함수도 랩핑 하고 제공합니다. 자세한 내용은 muru 의 답변을 참조하십시오. 리눅스에서는 적어도 이러한 함수들은 커널이 아닌 C 라이브러리에 의해 구현됩니다.
답변
부분적으로 이것은 exec
사용되는 특정 제품군 기능 에 따라 다릅니다 . execve
로, 스티븐 키트는 구체적으로 보여 주었다 만 적절한 오두막로 시작하는 올바른 바이너리 형식의 파일 또는 스크립트를 실행합니다.
그러나 , execlp
및 execvp
한 단계를 이동하십시오 오두막이 정확하지 않은 경우 파일이 함께 실행되는 /bin/sh
리눅스에서. 보낸 사람 man 3 exec
:
Special semantics for execlp() and execvp()
The execlp(), execvp(), and execvpe() functions duplicate the actions
of the shell in searching for an executable file if the specified
filename does not contain a slash (/) character.
…
If the header of a file isn't recognized (the attempted execve(2)
failed with the error ENOEXEC), these functions will execute the
shell (/bin/sh) with the path of the file as its first argument. (If
this attempt fails, no further searching is done.)
이것은 POSIX (강조 광산)에서 다소 지원됩니다 .
표준 개발자가 지적한 혼란의 잠재적 원인 중 하나는 프로세스 이미지 파일의 내용이 exec 함수 계열의 동작에 어떤 영향을 미치는지에 대한 것입니다. 다음은 취해진 조치에 대한 설명입니다.
프로세스 이미지 파일이이 시스템에 대해 유효한 실행 파일 (실행 가능하고 유효하며 적절한 권한이있는 형식) 인 경우 시스템은 파일을 실행합니다.
프로세스 이미지 파일에 적절한 권한이 있고 실행 가능하지만이 시스템에는 유효하지 않은 형식 (예 : 다른 아키텍처의 인식 된 바이너리) 인 경우 이는 오류이며 errno는 [EINVAL] (나중의 RATIONALE 참조)로 설정됩니다 [EINVAL]에서).
프로세스 이미지 파일에 적절한 권한이 있지만 달리 인식되지 않은 경우 :
이것이 execlp () 또는 execvp ()에 대한 호출 인 경우 프로세스 이미지 파일이 쉘 스크립트라고 가정하고 명령 인터프리터를 호출합니다.
이것이 execlp () 또는 execvp ()에 대한 호출이 아닌 경우 오류가 발생하고 errno가 [ENOEXEC]로 설정됩니다.
명령 인터프리터를 얻는 방법을 지정하지는 않지만 오류가 발생하도록 지정하지는 않습니다. 따라서 Linux 개발자는 이러한 파일을 실행할 수있었습니다 /bin/sh
(또는 이것은 이미 일반적인 관행이었고 방금 따라갔습니다).
FWIW에 대한 FreeBSD 맨 페이지exec(3)
도 비슷한 동작을 언급합니다.
Some of these functions have special semantics.
The functions execlp(), execvp(), and execvP() will duplicate the actions
of the shell in searching for an executable file if the specified file
name does not contain a slash ``/'' character.
…
If the header of a file is not recognized (the attempted execve()
returned ENOEXEC), these functions will execute the shell with the path
of the file as its first argument. (If this attempt fails, no further
searching is done.)
그러나 AFAICT 는 환경을보다 세밀하게 제어하기 위해 일반적인 쉘을 사용 execlp
하거나 execvp
직접 사용하지는 않습니다 . 모두를 사용하여 동일한 논리를 구현합니다 execve
.
답변
이것은 bash
파일의 소스에서 주석으로 Stephen Kitt 답변에 추가 될 수 있습니다 execute_cmd.c
.
어딘가에 디스크 파일에 정의 된 간단한 명령을 실행하십시오.
1. fork () 2. connect pipes 3. look up the command 4. do redirections 5. execve () 6. If the execve failed, see if the file has executable mode set.
그렇다면 디렉토리가 아닌 경우 내용을 쉘 스크립트로 실행하십시오.
답변
셸 스크립트로 실행되며 소스 가 아닙니다 (예 : 실행 된 파일에 설정된 변수는 외부에 영향을 미치지 않음). 아마도 하나의 쉘과 하나의 실행 가능한 형식 이 있었을 때 안개 낀 과거의 흔적 일 것 입니다. 실행 파일이 아니라 셸 스크립트 여야합니다.