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
.
- 쉘이 시작되었습니다.
- 개정 중
path
. - 명령을 수동으로 실행합니다
rehash
.
따라서 셸을 시작하면 그 안에 있는 모든 명령이 해시되고, path
수정하거나 실행하지 않는 한 다른 명령은 해시되지 않습니다.path
rehash
해시가 비활성화된 이유는 무엇일까요?
매뉴얼 페이지에서는 이러한 상황이 발생하는 조건을 지정합니다.
이 해싱 메커니즘은 사용되지 않습니다.
- 해싱이 명시적으로 해제된 경우
unhash
.- 쉘에
-f
매개변수를 제공하는 경우.
여기에 예가 있습니다. 해당 플래그로 셸을 시작하면 해싱 -f
이 hashstat
비활성화되어 비어 있게 됩니다.
$ 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
에 연결됨)의 순서에 따라 번호가 지정됩니다 .path
tcsh
$ echo $PATH
/usr/sbin:/usr/bin:/sbin:/bin
내 디렉토리의 PATH
번호는 다음과 같습니다.
/usr/sbin
/usr/bin
/sbin
/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
)
- 발견 중디렉토리 0(
passwd
- 발견 중디렉토리 1(
/usr/bin
).
- 발견 중디렉토리 1(
디버깅 수준을 높이면 얻을 수 있는 또 다른 이점은 내장된 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
나는 simplestrace sleep
및 을 시도했는데strace -f -e trace=execve sleep
둘 다 기본적으로 올바른 항목만 표시하지만 실패한 항목은 표시하지 않습니다.execve("/bin/sleep", ["sleep"], 0x7ffe0d773ff8 /* 32 vars */) = 0
을 실행하면 strace sleep
더 이상 위치를 검색하는 셸이 아니라 명령을 위한 sleep
것입니다 strace
. 쉘은 실제로 명령을 실행한 위치, 즉 줄의 첫 번째 단어(이 경우 strace
)만 검색합니다. 나머지 단어는 방금 명령에 전달된 인수로 처리됩니다. 따라서 명령을 strace
실행하기 전에 명령 자체는 .sleep
strace
PATH
쉘이 명령의 위치를 어떻게 찾는지 실제로 보려면 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
이는 버전과 배포판에서 컴파일된 방법에 따라 다를 수 있습니다.