내장 된 일부 'read'내장 쉘이`/ proc`의 파일에서 전체 줄을 읽지 못하는 이유는 무엇입니까?


19

일부 본쉘의 read에서 파일의 전체 라인을 읽을 수없는 내장 /proc(아래에서 실행해야 할 명령 zsh, 대체 $=shell$shell다른 껍질을) :

$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
  printf '[%s]\n' "$shell"
  $=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'       
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6

read표준은 표준 입력이 텍스트 파일이어야 한다고 요구하는데, 그 요구로 인해 다양한 동작이 발생합니까?


텍스트 파일 의 POSIX 정의를 읽고 확인을하십시오.

$ od -t a </proc/sys/fs/file-max 
0000000   6   0   2   1   6   0  nl
0000007

$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max

아무 없다 NUL의 내용의 문자 /proc/sys/fs/file-max, 또한 find일반 파일로보고 (이 버그인가 find?).

나는 껍질이 후드 아래에서 뭔가를 한 것 같아요 file.

$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty

답변:


31

문제는 /procLinux의 해당 파일이 텍스트 파일로 표시되는 것처럼 stat()/fstat()보이지만 그렇게 동작하지 않는다는 것입니다.

동적 데이터이기 때문에 하나 이상의 read()시스템 호출 만 수행 할 수 있습니다 (적어도 일부는 해당). 하나 이상의 작업을 수행하면 두 개의 다른 내용으로 된 두 개의 청크를 얻을 수 있으므로 대신 두 번째 청크 read()는 아무것도 반환하지 않습니다 (파일 끝을 의미) ( lseek()처음으로 돌아 가지 않고 시작 부분으로 돌아 가지 않는 한 ).

read유틸리티는 개행 문자 뒤로 읽어 확실하지 않은 것으로 한 바이트 한 번에 파일의 내용을 읽을 필요가있다. 즉 무엇 dash을 수행합니다

 $ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
 read(0, "1", 1)                         = 1
 read(0, "", 1)                          = 0

일부 쉘은 시스템 호출을 bash너무 많이하지 않아도되도록 최적화되어 read()있습니다. 그들은 먼저 파일을 찾을 수 있는지 확인하고, 그렇다면 파일을 읽은 후 줄 바꿈 바로 다음에 커서를 다시 넣을 수 있다는 것을 알고 덩어리로 읽습니다.

$ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max
lseek(0, 0, SEEK_CUR)                   = 0
read(0, "1628689\n", 128)               = 8

를 사용하면 bash128 바이트보다 큰 proc 파일에는 여전히 문제가 있으며 한 번의 읽기 시스템 호출에서만 읽을 수 있습니다.

bash-d옵션을 사용할 때 해당 최적화를 비활성화하는 것으로 보입니다 .

ksh93가짜가 될 정도로 최적화를 더욱 강화합니다. ksh93의의는 read다시 찾아 않지만, 다음에 대한 읽기 추가 데이터를 기억 read다음, 그래서 read(또는 같은 데이터를 읽을의 다른 내장 매크로의 cat또는 head)도 시도하지 않는다 read(즉, 데이터가 수정 된 경우에도 데이터 사이에 다른 명령) :

$ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 2
$ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 st

아, 그렇습니다. strace기반 설명은 이해하기 훨씬 쉽습니다!
Stephen Kitt

감사합니다. 동적 데이터 가 적합합니다. 그렇다면 쉘은 어떻게 동적 데이터를 감지합니까? 내가 cat /proc/sys/fs/file-max | ...하면 문제가 사라졌습니다.
cuonglm

3
쉘이 감지하지 못합니다. 동적 데이터라는 사실 은 동일한 파일에 대한 procfs여러 연속 read(2)호출을 처리 할 수 ​​없음을 의미 합니다. 동작은 쉘에 의존하지 않습니다. 사용 cat및 파이핑 작업 cat은 파일을 충분히 큰 청크로 읽으 므로 작동 합니다. read그런 다음 쉘의 내장은 파이프에서 한 번에 한 문자 씩 읽습니다.
Stephen Kitt

1
에 약간 더티 해결 방법이 mksh있습니다. read -N 10 a < /proc/sys/fs/file-max
Ipor Sircer

1
@IporSircer. 과연. 비슷한 해결 방법은 다음과 함께 작동하는 것 같습니다 zsh: read -u0 -k10(또는 사용 sysread; $mapfile[/proc/sys/fs/file-max]해당 파일을 mmap편집 할 수 없으므로 작동하지 않습니다 ). 어쨌든 어떤 쉘이든 항상 할 수 있습니다 a=$(cat /proc/sys/fs/file-max). 일부 포함으로 mksh, zsh그리고 ksh93, a=$(</proc/sys/fs/file-max)또한 작품과 독서를 할 수있는 프로세스를 포크하지 않습니다.
Stéphane Chazelas

9

왜 그런지 알고 싶다면 ? 이것은 커널 소스 에서 답을 볼 수 있습니다 :

    if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
            *lenp = 0;
            return 0;
    }

기본적으로 탐색 ( *ppos0 아님)은 !write숫자 인 sysctl 값의 읽기 ( )에 대해 구현되지 않습니다 . 에서 읽기를 수행 할 때마다 동일한 파일 의 구성 테이블 항목에서 해당 /proc/sys/fs/file-max루틴 __do_proc_doulongvec_minmax()이 호출 됩니다.file-max

검색을 허용하는 /proc/sys/kernel/poweroff_cmdvia 와 같은 다른 항목을 proc_dostring()사용하면 dd bs=1문제없이 쉘에서 읽을 수 있습니다.

커널 2.6 이후 대부분의 /proc읽기는 seq_file 이라는 새로운 API를 통해 구현되었으며 검색을 지원하므로 읽기 /proc/stat가 문제를 일으키지 않아야합니다. /proc/sys/구현은, 우리가 볼 수 있듯이,이 API를 사용하지 않습니다.


3

첫 번째 시도에서 이것은 실제 Bourne Shell 또는 그 파생 상품 (sh, bosh, ksh, heirloom)보다 적은 값을 반환하는 쉘의 버그처럼 보입니다.

원래 Bourne Shell은 블록 (64 바이트)을 읽으려고합니다. 최신 Bourne Shell 변형은 128 바이트를 읽지 만 줄 바꿈 문자가 없으면 다시 읽기 시작합니다.

배경 : / procfs 및 이와 유사한 구현 (예 : 탑재 된 /etc/mtab가상 파일)에는 동적 내용이 있으며 stat()호출로 인해 동적 내용이 먼저 재생성 되지 않습니다 . 따라서 이러한 파일의 크기 (읽기에서 EOF까지)는 stat()반환 되는 크기와 다를 수 있습니다 .

POSIX 표준에 따라 유틸리티 는 언제든지 짧은 읽기 를 기대해야하므로 read(), 주문 된 바이트 수 보다 적은 값을 리턴 하는 것이 EOF 표시 라고 생각하는 소프트웨어 가 손상되었습니다. 올바르게 구현 된 유틸리티 read()는 0이 반환 될 때까지 예상보다 적게 반환되는 경우 다시 호출 합니다. read내장의 경우 물론 a 가 보일 때 까지 EOF 또는 읽을 때까지 읽는 것이 충분합니다 NL.

실행 truss하거나 트러스 복제 를 수행 하는 경우 6실험 에서만 반환 되는 셸에 대한 잘못된 동작을 확인할 수 있습니다 .

이 특별한 경우에는 Linux 커널 버그 인 것 같습니다.

$ sdd -debug bs=1 if= /proc/sys/fs/file-max 
Simple copy ...
readbuf  (3, 12AC000, 1) = 1
writebuf (1, 12AC000, 1)
8readbuf  (3, 12AC000, 1) = 0

sdd: Read  1 records + 0 bytes (total of 1 bytes = 0.00k).
sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k).

리눅스 커널은 두 번째 로 0 을 반환 read하고 이것은 물론 올바르지 않습니다.

결론 : 충분한 양의 데이터를 먼저 읽으려고 시도하는 쉘은이 Linux 커널 버그를 유발하지 않습니다.


좋아, 리눅스 커널 버그에 대한 새로운 검증으로 답변을 종료했습니다.
schily

버그가 아니라 기능입니다!
Guntram Blohm은 Monica를

이것은 정말 이상한 주장입니다.
schily

문서화 된 경우 기능 일 것입니다. kernel.org/doc/Documentation/filesystems/proc.txt를 읽으면 동작에 대한 문서가 없습니다. 즉, 구현자가 의도 한대로 작동하는 것이기 때문에 이것이 버그로 간주되면 구현이 아닌 디자인의 버그입니다.
Charles Duffy

0

/ proc 아래의 파일은 때로는 NULL 문자를 사용하여 파일 내부의 필드를 구분합니다. 읽기가 이것을 처리 할 수없는 것 같습니다.

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