명령이 해시되는 방식tcsh

명령이 해시되는 방식tcsh

tcsh 환경에서는 명령 해싱이 기본적으로 비활성화되어 있는 것으로 보이며 전반적으로 활성화하는 것이 허용되지 않습니다. 대신, 모두 while 루프를 포함하는 개별 스크립트에서 명령 해싱을 활성화하여 첫 번째 반복에서 $PATH에 정의된 모든 경로를 반복할 수 있고 후속 반복에서 내부 스크립트의 정확한 경로에 도달할 수 있기를 원합니다. 해시 테이블. 목적은 감사 서비스에서 캡처한 실패한 execve 호출 수를 줄이는 것입니다.

첫 번째 질문은 내부 해시 테이블을 출력하기 위해 tcsh에 "hash"와 유사한 명령이 있습니까? Hashstat이 작동하지 않는 것 같습니다. 프롬프트에 아무것도 출력하지 않습니다. 아마도 해싱이 비활성화되어 있기 때문일까요? 무언가를 인쇄하도록 하면 특정 명령이 아닌 해시 버킷의 수와 크기만 인쇄됩니다.

주요 문제는 스크립트 시작 부분에 "rehash"를 추가해 보았는데, 이는 명령당 execve 호출 수를 ~5에서 ~2(첫 번째 반복에서도)로 줄이는 데 도움이 되었습니다. 어떤 이유로든 항상 "/sbin"에서 명령을 먼저 실행하려고 시도합니다. rehash를 실행한 후에도 여전히 잘못된 경로에서 명령을 실행하려고 하는 이유를 확인하기 위해 무엇을 확인해야 하는지에 대한 제안 사항이 있습니까? 아니면 스크립트 내에서 명령 해싱을 활성화하는 다른 방법이 있습니까?

반면에 측면 문제는 해시 테이블이 비활성화된 경우에도 bash가 여전히 올바른 경로를 찾을 수 있다는 것입니다. 명령 해싱 없이 어떻게 이를 수행할 수 있는지 아시나요?

마지막으로 strace는 감사에서 캡처한 실패한 execve 호출을 캡처하지 않았습니다. 나는 simple strace sleep및 을 시도했는데 strace -f -e trace=execve sleep둘 다 기본적으로 올바른 항목만 표시하지만 실패한 항목은 표시하지 않습니다.

execve("/bin/sleep", ["sleep"], 0x7ffe0d773ff8 /* 32 vars */) = 0.

답변1

꽤 복잡한 질문인데 설명해 드리겠습니다.

명령이 해시되는 방식tcsh

명령 해시는 tcsh의 명령 해시와 매우 다릅니다 bash. 에서는 bash처음으로 명령을 실행할 때마다 PATH환경 변수에서 해당 위치를 검색하고 캐시합니다. 다음에 동일한 명령을 실행하면 해시된 전체 경로가 사용됩니다.

관련 부분입니다tcsh 매뉴얼 페이지:

rehash

path변수에 있는 디렉터리 내용의 내부 해시 테이블이 다시 계산됩니다. 로그인한 동안 디렉터리에 새 명령이 추가되는 경우 path필요합니다 . 이는 자신의 디렉토리 중 하나에 명령을 추가하거나 시스템 프로그래머가 시스템 디렉토리 중 하나의 내용을 변경하는 경우에만 필요합니다. 또한 물결표 확장으로 작성된 홈 디렉토리의 캐시를 플러시합니다.

path

실행 가능한 명령을 찾을 디렉터리 목록입니다. [...] 옵션 -c-t옵션도 제공되지 않는 쉘경로의 디렉터리 내용은 읽은 후 해시되고 ~/.tcshrc매번 path재설정됩니다 .. path셸이 활성화된 동안 디렉터리에 새 명령을 추가하는 경우 rehash해당 명령을 찾으려면 셸에서 a를 실행해야 할 수도 있습니다.

tcsh즉, 변수 디렉토리에 있는 모든 명령은 다음과 같은 경우에만 해시됩니다 path.

  1. 쉘이 시작되었습니다.
  2. 개정 중 path.
  3. 명령을 수동으로 실행합니다 rehash.

따라서 셸을 시작하면 그 안에 있는 모든 명령이 해시되고, path수정하거나 실행하지 않는 한 다른 명령은 해시되지 않습니다.pathrehash

해시가 비활성화된 이유는 무엇일까요?

매뉴얼 페이지에서는 이러한 상황이 발생하는 조건을 지정합니다.

이 해싱 메커니즘은 사용되지 않습니다.

  1. 해싱이 명시적으로 해제된 경우 unhash.
  2. 쉘에 -f매개변수를 제공하는 경우.

여기에 예가 있습니다. 해당 플래그로 셸을 시작하면 해싱 -fhashstat비활성화되어 비어 있게 됩니다.

$ tcsh -f
> hashstat

어딘가에서 명령을 실행하는 경우에도 동일한 일이 발생합니다 unhash.

그러나 두 경우 모두 "rehash"를 실행하여 활성화할 수 있습니다.

> rehash
> hashstat
512 hash buckets of 8 bits each

tcsh해싱이 활성화된 경우에도 일부 명령이 여전히 다른 경로에서 실행되는 이유는 무엇입니까?

이해하다hashstat

출력을 살펴보겠습니다 hashstat.

$ hashstat
512 hash buckets of 8 bits each

512개의 해시 버킷이 있다고 나와 있습니다. tcsh해시 함수를 사용하여 각 명령의 해시 ID를 계산합니다. 이 경우 값은 0에서 511 사이입니다. 이는 일부 명령이 동일한 해시를 가질 수 있음을 의미합니다. 버킷 및 비트의 초기 수는 에 따라 달라질 수 있습니다 PATH.

두 명령 A와 B가 동일한 해시 값을 가지고 있다고 가정합니다(두 명령의 해시 값이 123이라고 가정). 명령 A가 있고 /bin명령 B가 있다고 가정합니다 /usr/bin. 그러면 어떻게 되나요?

이는 해시 ID가 123인 버킷에 두 경로(둘 다 /bin및 ) 가 있음을 의미합니다 /usr/bin. A 또는 B를 실행하면 성공할 때까지 exec두 디렉터리(에 있는 순서대로)에서 해당 명령을 실행 하려고 시도합니다 . PATH이 답변 아래에 예가 표시됩니다. 하지만 이제 이야기해보자 rehash.

rehash"비밀" 매개변수

rehash내장 함수에는 tcsh소스 코드에서만 볼 수 있는 문서화되지 않은 일부 "비밀" 선택적 매개변수가 있습니다. 옵션은 다음과 같습니다:

rehash [hashlength [hashwidth [debug]]]

예를 들어 다음과 같은 방법으로 해시 버킷 수를 변경할 수 있습니다.

$ hashstat
512 hash buckets of 8 bits each   # At the beginning, 512 buckets

$ rehash 4096                     # increasing the bucket number

$ hashstat
4096 hash buckets of 8 bits each  # Now there are 4096 buckets instead of 512

해시 너비에 대해서는 길이가 0으로 설정된 경우에만 작동하므로 설명하지 않겠습니다(너비는 해시 길이를 계산하기 위해 내부적으로 사용됩니다). 그 외에는 아무런 의미가 없습니다.

중요한 팁

테이블 길이를 늘리면 서로 다른 명령 간에 발생할 수 있는 충돌 수를 최소화하는 데 도움이 됩니다. 따라서 일부 테스트를 수행하는 경우 시험해보고 몇 가지 개선 사항이 있는지 확인할 수 있습니다.

그래도,진짜재미있는 부분은 아직 오지 않았습니다.

내부 해시 테이블 표시

마지막 매개변수를 1 또는 3으로 설정하여 해시 작업 디버깅을 늘릴 수 있습니다. 그것이 어떻게 작동하는지 봅시다.

$ rehash 0 0 3 
hash=19   dir=0  prog=addgnupghome
hash=0    dir=0  prog=addpart
hash=206  dir=0  prog=agetty
hash=498  dir=0  prog=alternatives
hash=463  dir=0  prog=applygnupgdefaults
hash=323  dir=0  prog=blkdiscard
hash=342  dir=0  prog=blkid
hash=277  dir=0  prog=blkzone
hash=410  dir=0  prog=blockdev
hash=500  dir=0  prog=cfdisk
[...]

모든 명령에 대한 해시가 무엇인지, 해시가 어디에 있는지 정확하게 보여줍니다.

예를 들어, 이 명령의 addgnupghome해시 값은 19이고 디렉터리 번호 0에 있습니다. 카탈로그 번호 0은 무엇입니까?

디렉토리는 환경 변수( 의 쉘 변수 PATH에 연결됨)의 순서에 따라 번호가 지정됩니다 .pathtcsh

$ echo $PATH
/usr/sbin:/usr/bin:/sbin:/bin

내 디렉토리의 PATH번호는 다음과 같습니다.

  1. /usr/sbin
  2. /usr/bin
  3. /sbin
  4. /bin

따라서 명령이 디렉토리 번호 0 에 있으면 addgnupghome해당 명령이 에 있다는 의미입니다 /usr/sbin.

rehash 명령의 출력(디버깅을 3으로 설정한 후)을 파일에 저장해 보겠습니다.

$ rehash > rehash.log

이제 특정 해시를 살펴보겠습니다.

$ grep hash=498 rehash.log
hash=498  dir=0  prog=alternatives
hash=498  dir=1  prog=passwd

동일한 해시 값(498)에 매핑된 두 개의 명령이 있습니다.

  • alternatives
    • 발견 중디렉토리 0( /usr/sbin)
  • passwd
    • 발견 중디렉토리 1( /usr/bin).

디버깅 수준을 높이면 얻을 수 있는 또 다른 이점은 내장된 where("별칭, 내장 및 실행 파일을 포함하여 알려진 모든 명령 인스턴스를 보고합니다. path"). 일반적으로 실행 파일이 발견된 위치만 표시하지만 디버깅을 늘리면 놓친 위치도 표시됩니다.

$ where alternatives
/usr/sbin/alternatives
hash miss: /usr/bin/alternatives

$ where passwd
hash miss: /usr/sbin/passwd
/usr/bin/passwd

이 두 명령에 대해 두 디렉터리를 검색하는 것을 볼 수 있습니다: /usr/sbin/usr/bin!

로 확인하세요 strace.

당신은 다음과 같이 썼습니다:

마지막으로 감사에서 캡처된 실패한 호출은 strace캡처되지 않습니다 . execve나는 simple strace sleep및 을 시도했는데 strace -f -e trace=execve sleep둘 다 기본적으로 올바른 항목만 표시하지만 실패한 항목은 표시하지 않습니다.

execve("/bin/sleep", ["sleep"], 0x7ffe0d773ff8 /* 32 vars */) = 0

을 실행하면 strace sleep더 이상 위치를 검색하는 셸이 아니라 명령을 위한 sleep것입니다 strace. 쉘은 실제로 명령을 실행한 위치, 즉 줄의 첫 번째 단어(이 경우 strace)만 검색합니다. 나머지 단어는 방금 명령에 전달된 인수로 처리됩니다. 따라서 명령을 strace실행하기 전에 명령 자체는 .sleepstracePATH

쉘이 명령의 위치를 ​​어떻게 찾는지 실제로 보려면 strace다른 터미널에서 쉘에 연결하고 명령을 실행해야 합니다.

tcsh예를 들어, PID가 21033인 프로세스가 있습니다 . 다른 터미널에서 strace해당 pid에 연결하고 있습니다.

$ strace -f -qq -e trace=execve -p 21033

alternatives연결된 셸에서 실행 하면 이 해시 목록의 첫 번째 디렉터리에서 즉시 해당 셸을 찾습니다.

[pid 19134] execve("/usr/sbin/alternatives", ["alternatives", "--help"], [/* 125 vars */]) = 0

그러나 passwd명령은 두 번째 디렉터리에 있으며 execve첫 번째 디렉터리에서는 실패합니다.

[pid 20919] execve("/usr/sbin/passwd", ["passwd", "-h"], [/* 125 vars */]) = -1 ENOENT (No such file or directory)
[pid 20919] execve("/usr/bin/passwd", ["passwd", "-h"], [/* 125 vars */]) = 0

다음은 해시를 하나의 명령에만 매핑하는 반대 예입니다.

$ grep hash=102 rehash.log
hash=102  dir=1  prog=curl   # Only curl has hash 102

$ where curl
/usr/bin/curl                # And indeed "where" only tries one dir

부인 성명

내가 여기에 쓰는 모든 내용은 내 경험을 바탕으로 작성되었습니다.tcsh 소스 코드내 환경에서 테스트했습니다(내 버전은 6.20.00입니다). tcsh이는 버전과 배포판에서 컴파일된 방법에 따라 다를 수 있습니다.

관련 정보