파일 시스템을 탐색 할 때 Unix는 사용자의 작업 디렉토리를 어떻게 추적합니까?


29

유닉스 시스템에서 쉘에 로그인하고 명령을 내리기 시작한다고 가정 해보십시오. 처음에는 사용자의 홈 디렉토리에서 시작합니다 ~. 거기 cd에서 디렉토리까지 내려갈 수 있습니다 Documents.

여기서 작업 디렉토리를 변경하는 명령은 직관적으로 이해하기가 매우 간단합니다. 상위 노드에는 액세스 할 수있는 하위 노드 목록이 있으며 아마도 검색의 (최적화 된) 변형을 사용하여 하위 노드의 존재를 찾습니다. 사용자가 입력 한 이름을 입력하면 작업 디렉토리가 이에 맞게 "변경"됩니다. 내가 틀렸다면 정정하십시오. 쉘이 단순히 "순진하게"사용자가 원하는대로 정확하게 디렉토리에 액세스하려고 시도하고 파일 시스템이 어떤 유형의 오류를 리턴하면 쉘은 이에 따라 응답을 표시하는 것이 더 간단 할 수도 있습니다.

그러나 내가 관심있는 것은 디렉토리, 즉 부모 또는 부모의 부모를 탐색 할 때 동일한 프로세스가 어떻게 작동하는지입니다.

Documents전체 파일 시스템 트리에서 해당 이름을 가진 많은 디렉토리 중 하나 인 "알지 못하는"위치를 알 수 있다면 Unix는 다음 위치를 어떻게 결정합니까? 그것을 참조 pwd하고 조사합니까? 그렇다면 pwd현재 탐색 상태를 어떻게 추적합니까?


답변:


76

다른 답변은 지나치게 단순화 된 것인데, 각각은 이야기의 일부만 제시하며 몇 가지 점에서 잘못되었습니다.

작업 디렉토리를 추적하는 가지 방법 이 있습니다 .

  • 모든 프로세스에 대해 해당 프로세스를 나타내는 커널 공간 데이터 구조에서 커널은 작업 디렉토리의 vnode와 해당 프로세스의 루트 디렉토리에 대한 두 개의 vnode 참조를 저장합니다. 전자 참조는 chdir()fchdir()시스템 호출에 의해 설정되고 후자는에 의해 설정됩니다 chroot(). /procLinux 운영 체제에서 또는 fstatFreeBSD 의 명령 등을 통해 간접적으로 볼 수 있습니다 .

    % fstat -p $$ | 헤드 -n 5
    사용자 CMD PID FD 마운트 숫자 모드 SZ | DV R / W
    JdeBP ZSH 92648 텍스트 / 24958 -R-XR-XR-X 702360R
    JdeBP zsh 92648 ctty / dev 148 crw--w ---- pts / 4 rw
    JdeBP ZSH 92648 WD / usr / home / JdeBP 4 drwxr-xr-x 124 r
    JdeBP ZSH 92648 루트 / 4 drwxr-xr-x 35 r
    % 

    경로 이름 분석이 작동하면 경로가 상대인지 또는 절대인지에 따라 참조 된 vnode 중 하나에서 시작됩니다. ( …at()열린 (디렉토리) 파일 디스크립터가 세 번째 옵션으로 참조하는 vnode에서 경로 이름 분석을 시작할 수 있는 시스템 호출 제품군이 있습니다.)

    마이크로 커널 Unices에서 데이터 구조는 응용 프로그램 공간에 있지만 이러한 디렉토리에 대한 열린 참조를 유지하는 원리는 동일하게 유지됩니다.

  • 내부적으로, 예를 들면 Z, 콘, Bourne의 다시, C 및 Almquist 쉘로 쉘 내에, 쉘 부가 내부 변수 문자열의 문자열 처리를하여 작업 디렉토리 추적. 호출 할 때마다이 작업을 수행합니다 chdir().

    상대 경로 이름으로 변경되면 해당 이름을 추가하기 위해 문자열을 조작합니다. 절대 경로 이름으로 변경되면 문자열이 새 이름으로 바뀝니다. 두 경우 모두 제거 할 문자열 ...구성 요소 를 조정하고 링크 된 이름으로 대체하는 심볼릭 링크를 추적합니다. ( 예를 들어 Z 쉘의 코드는 다음과 같습니다 .)

    내부 문자열 변수의 이름은 추적되는 쉘 변수 의 이름 PWD(또는 cwd은 C 껍질). 이것은 일반적으로 환경 변수 ( PWD)를 쉘이 생성 한 프로그램에 내 보냅니다.

추적 일들이 두 가지 방법이 계시되어 -P-L받는 옵션 cdpwd쉘 내장 명령 및 내장 쉘 '의 차이에 의해 pwd명령과 모두 /bin/pwd명령 및 내장 pwd같은 것들의 명령 (다른 사람의 사이에) VIM과 NeoVIM.

% mkdir a; ln -sab 
% (cd b; pwd; / bin / pwd; printenv PWD)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / a
/ usr / home / JdeBP / b
% (cd b; pwd -P; / bin / pwd -P)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; pwd -L; / bin / pwd -L)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / b
% (cd -P b; pwd; / bin / pwd; printenv PWD)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; PWD = / hello // bin / pwd -L)
/ usr / home / JdeBP / a
% 

보다시피, "논리적"작업 디렉토리를 얻는 것은 PWD쉘 변수 (또는 쉘 프로그램이 아닌 경우 환경 변수) 를 조사하는 문제입니다 . "실제"작업 디렉토리를 얻는 것은 getcwd()라이브러리 함수 를 호출하는 문제입니다 .

옵션이 사용될 /bin/pwd때 프로그램 의 작동 -L은 다소 미묘합니다. 그것은 믿을 수 의 값 PWD이 상속 한 것으로 환경 변수를. 결국, 그것은 쉘에 의해 호출 될 필요가 없으며 개재 프로그램은 PWD환경 변수가 항상 작업 디렉토리의 이름을 추적 하게하는 쉘의 메커니즘을 구현하지 않았을 수도 있습니다 . 아니면 누군가 내가 방금했던 일을 할 수도 있습니다.

POSIX 표준에 따르면 시스템 호출 추적에서 볼 수 있듯이 주어진 이름이 name PWD과 동일한 것을 생성 하는지 확인하십시오 ..

% ln -sac 
% (cd b; 트러스 / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ( "/ usr / home / JdeBP / b", { mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0) 
stat ( ".", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0)
/ usr / home / JdeBP / b
% (cd b; PWD = / usr / local / etc 트러스 / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ( "/ usr / local / etc" , {mode = drwxr-xr-x, inode = 14835, size = 158, blksize = 10240}) = 0 (0x0) 
stat ( ".", {mode = drwxr-xr-x, inode = 120932, size = 2 , blksize = 131072}) = 0 (0x0)
__getcwd ( "/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / hello / 트러스 / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ( "/ hello / there", 0x7fffffffe730) ERR # 2 '해당 파일이나 디렉토리가 없습니다' 
__getcwd ( "/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / usr / home / JdeBP / c truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ( "/ usr / home / JdeBP / c ", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0) 
stat (". ", {mode = drwxr-xr-x, inode = 120932 , 크기 = 2, blksize = 131072}) = 0 (0x0)
/ usr / home / JdeBP / c
%

보시다시피 : getcwd()불일치를 감지 한 경우 에만 호출 합니다. PWD실제로 동일한 디렉토리의 이름을 지정하지만 다른 경로로 문자열을 설정 하여 속일 수 있습니다 .

getcwd()라이브러리 함수는 고유의 권리로 될 수 있습니다. 그러나 précis :

  • 원래는 라이브러리 함수였으며, 디렉토리에서 작업 디렉토리를 반복적으로 검색하여 작업 디렉토리에서 루트까지 경로 이름을 작성했습니다 ... ..작업 디렉토리와 동일한 루프에 도달 했거나 다음 디렉토리를 열려고하는 중에 오류가 발생했을 때 중지되었습니다 ... 이것은 덮개 아래에 많은 시스템 호출이 될 것입니다.
  • 오늘날 상황은 약간 더 복잡합니다. FreeBSD의에 (예를 들어,이뿐만 아니라 다른 운영 체제에 대한 진실되고), 그것은 이다 이전에 주어진 시스템 호출 추적에서 볼 수 있듯이, 실제 시스템 호출. 작업 디렉토리 vnode에서 루트까지의 모든 순회는 단일 시스템 호출에서 수행되며, 경로 이름 구성 요소 조회를 훨씬 효율적으로 수행하기 위해 커널 모드 코드의 디렉토리 항목 캐시에 대한 직접 액세스와 같은 기능을 활용합니다.

    그러나 FreeBSD 및 다른 운영 체제에서도 커널 문자열로 작업 디렉토리를 추적 하지 않습니다 .

탐색 ..은 다시 그 자체로 주제입니다. 또 다른 PRECIS : (이미이가되어 언급,이기는하지만 종래 디렉토리 만 하지 실제를 포함 필수) ..디스크의 디렉토리 데이터 구조는, 커널은 각 디렉토리의 vnode에 자신의 부모 디렉토리를 추적하고 있습니다 따라서 탐색 ..어떤의 vnode에 작업 디렉토리. 이 대답의 범위를 벗어난 마운트 지점과 변경된 루트 메커니즘으로 인해 다소 복잡합니다.

곁에

실제로 Windows NT도 비슷한 일을합니다. 프로세스 당 단일 작업 디렉토리가 있으며 SetCurrentDirectory()API 호출에 의해 설정되고 해당 디렉토리에 대한 (내부) 열린 파일 핸들을 통해 커널이 프로세스별로 추적합니다. Win32 프로그램 (명령 해석기뿐만 아니라 모든 Win32 프로그램)이 디렉토리를 변경할 때마다 추가하거나 덮어 쓰는 여러 작업 디렉토리 (드라이브 당 하나)의 이름을 추적하는 데 사용하는 환경 변수 세트가 있습니다 .

일반적으로 Unix 및 Linux 운영 체제의 경우와 달리 Win32 프로그램은 이러한 환경 변수를 사용자에게 표시하지 않습니다. 그러나 Windows NT에서 실행되는 Unix와 유사한 서브 시스템 SET에서 특정 방식으로 명령 인터프리터의 명령 을 사용하여이를 볼 수 있습니다.

추가 자료


1
이것은 내가 예상했던 것보다 훨씬 더 많은 것입니다. 읽어 주셔서 감사합니다.
ReactingToAngularVues

doc.cat-v.org/plan_9/4th_edition/papers/lexnames.. 는 Plan9 와 관련된 문제 중 일부에 대해 이야기합니다 .
이카루스

@ JdeBP : 아마도 뭔가 빠졌을 것입니다. “내부적으로…, bash,… 및… 안에서 쉘 은 내부 문자열 변수의 문자열 조작을 사용하여 작업 디렉토리를 추가로 추적합니다. … 문자열을 제거 .하여 ..구성 요소 를 제거 하고 심볼릭 링크를 쫓아 링크 된 이름으로 대체합니다. … 내부 문자열 변수의 이름은 PWD…”(강조 추가) 라는 쉘 변수에 의해 추적됩니다 . … (계속)
G-Man, 'Reinstate

(계속)… 그러나 예제는 명령 뒤에 PWD= 를 표시합니다. 비록 심볼릭 링크 이지만 쉘은 링크를 “추적”하지 않습니다 . 오해 했습니까, 아니면 잘못 읽었습니까? …/bcd bbaa -> b
G-Man, 'Reinstate

나는 단순히 부수적 인 측면을 살펴보고 자세한 내용은 코드를 지적했다. 기호 링크를 추적하기로 결정한시기 및 방법에 대해서는 다양한 쉘 매뉴얼을 참조하십시오. Z 쉘은 의사 결정 공식의 일부인 쉘 옵션을 간단하게 호출합니다 CHASE_LINKS.
JdeBP

1

커널은 디렉토리 나 파일 이름을 추적하지 않습니다. 파일 또는 디렉토리는 커널에서 inode / 장치 쌍으로 표시됩니다. 추천 시스템 호출 chdir(), open()등이 될 수 절대 파라미터로서 패스 (예를 취 /etc/passwd), 또는 현재의 디렉토리에 상대적 (예 : Documents, ..). 프로세스가 실행되면 현재 작업 디렉토리에서 chdir("Documents")조회가 수행 Documents되고 프로세스의 작업 디렉토리가이 디렉토리를 참조하도록 업데이트됩니다. 커널의 관점에서 볼 때 ".."라는 이름에는 특별한 것이 없습니다. 파일 시스템 ..에서 부모 디렉토리를 나타내는 규칙 일뿐 입니다.

getcwd()함수는 시스템 호출이 아니라 루트 디렉토리까지 작동해야하는 라이브러리 함수로서 경로 구성 요소의 이름을 기록합니다.


0

흥미롭게도 전통적 cd ..으로는보다 훨씬 간단합니다 pwd. 이름 ..이 지정된 디렉토리 는 파일 시스템에 명시 적으로 배치됩니다. 시스템은 현재 디렉토리의 장치 / 노드를 추적하므로 cd ..시스템 호출 chdir("..")은 현재 디렉토리의 inode에 속하는 파일에서 ".."이름을 찾고 현재 디렉토리의 장치 / 노드를 거기에서 발견 된 가치.

pwd(보다 정확하게는 /bin/pwd) ..링크를 연속적으로 따르고 원래 디렉토리가 루트 디렉토리에 도달 할 때까지 (즉, ..항목을 포함하지 않음) 해당 이름 목록을 반대로 어셈블 링 할 때까지 해당 디렉토리를 읽습니다 .

이제 이것은 원래의 저수준 기본 동작입니다. 실제 쉘 명령 pwd은 현재 경로 이름을 캐싱하는 다양한 기술에 의존합니다. 그러나 핵심은 실제로 알려진 유일한 inode입니다. 즉, 일단 심볼릭 링크가 디렉토리 탐색에 사용되면 현재 쉘 및 시스템의 현재 작업 디렉토리 이름 개념 /bin/pwd이 분기 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.