Linux에는 파일 열기를 처리하는 시스템 계층/스크립트가 있습니까?

Linux에는 파일 열기를 처리하는 시스템 계층/스크립트가 있습니까?

Linux에는 파일 열기에 대한 프로그램 요청을 처리하는 레이어/스크립트가 있습니까?

Bash에서 파일 설명자를 열 때처럼: exec 3 <>/documents/foo.txt​​또는 텍스트 편집기가 열릴 때와 같습니다./documents/foo.txt

나는 편집자가 스스로 읽기/쓰기 액세스를 위해 "파일을 열 수 있다"는 것을 믿을 수 없습니다.

나는 오히려 이것이 "레이어"에 대한 요청이라고 생각합니다(init.d 스크립트?)는 특정 양의 파일만 여는 것으로 시작할 수 있으며, 열린 파일에 대한 액세스 종류, 열린 프로세스 등에 대한 탭을 유지합니다.

답변1

이 계층은 Linux 및 역사적인 Unix 설계에서 크게 벗어나지 않는 기타 시스템(그리고 대부분의 Unix가 아닌 운영 체제에서도 마찬가지)의 커널 내부에 있습니다.

커널의 이 부분을 커널이라고 합니다.VFS(가상 파일 시스템) 계층. VFS의 역할은 열린 파일에 대한 정보를 관리하는 것입니다.파일 설명자,열린 파일 설명및 디렉토리 항목), 파일 경로 구문 분석( 및 해석 /) ., ..디렉토리 항목에 대한 작업을 올바른 파일 시스템 드라이버로 전달합니다.

대부분의 파일 시스템 드라이버도 커널에 있지만퓨즈파일 시스템 드라이버를 사용하면 이 기능을 커널 외부에 위임할 수 있습니다. 예를 들어 디스크 파일 시스템이루프 장치.

답변2

리눅스에서 파일 열기는 커널에 의해 직접 처리됩니다.하지만 프로세스에 영향을 주고 연구하기 위해 할 수 있는 일이 몇 가지 있습니다.


Linux 스토리지 스택 다이어그램


시스템 호출

위에서부터 파일과 상호 작용하는 데 사용하는 인터페이스 응용 프로그램이 다음과 같은 것을 볼 수 있습니다.시스템 호출.

열려 있는,읽다그리고쓰다당신이 기대하는 대로 하세요.통계파일을 열지 않고 파일에 대한 정보를 반환합니다.

strace를 사용하여 프로그램의 파일 관련 syscall 사용을 연구할 수 있습니다.

$ strace -e trace=%file /bin/ls /etc
[...]
stat("/etc", {st_mode=S_IFDIR|0755,  ...}) = 0
openat(AT_FDCWD, "/etc", O_RDONLY...) = 3

이는 에 의해 발생한 syscall을 분석하여 해당 디렉토리에서 및 가 호출되었음을 ls /etc보여줍니다 .statopenat/etc

디렉토리에서 파일 작업을 호출하는 이유가 궁금할 것입니다. UNIX에서는 디렉토리도 파일입니다. 사실은모든 것이 파일이다!


파일 설명자

openat() = 3위의 출력에서 ​​에 대해 궁금할 수도 있습니다 .

UNIX에서 열린 파일은 다음과 같이 표시됩니다.파일 설명자, 이는 특정 프로세스에 의해 열린 파일을 고유하게 표현한 것입니다. 파일 설명자 0, 1, 2는 일반적으로 다음을 위해 예약되어 있습니다.표준 스트림(사용자 입력/출력)이므로 처음으로 열린 파일은 3이 됩니다.

다음을 사용하여 특정 프로세스에 대해 열린 파일 설명자 목록을 얻을 수 있습니다.lsof(에스영형에프파일):

$ cat /dev/urandom > /dev/null &
[1] 3242
$ lsof -p 3242
COMMAND  PID      USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
...
cat     3242 user         0u   CHR  136,0      0t0      3 /dev/pts/0
cat     3242 user         1w   CHR    1,3      0t0   1028 /dev/null
cat     3242 user         2u   CHR  136,0      0t0      3 /dev/pts/0
cat     3242 user         3r   CHR    1,9      0t0   1033 /dev/urandom

열에 FD는 액세스와 함께 파일 설명자 번호가 표시됩니다.

당신은 또한 사용할 수 있습니다fuser특정 파일을 보유하는 프로세스를 검색하려면 다음을 수행하십시오.

$ fuser /dev/urandom
/dev/urandom:         ...  3242  ...

프로세스 정보 의사 파일 시스템 - /proc

지금쯤이면 다음과 같은 사항이 궁금할 것입니다.하지만 lsof처음에 어떤 파일이 열려 있는지 어떻게 알 수 있나요??

글쎄, 살펴 보자!

$ strace -e trace=%file lsof -p 3242
...
stat("/proc/3242/", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
openat(AT_FDCWD, "/proc/3242/stat", O_RDONLY) = 4
...
openat(AT_FDCWD, "/proc/3242/fd", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4
readlink("/proc/3242/fd/0", "/dev/pts/0", 4096) = 10
lstat("/proc/3242/fd/0", {st_mode=S_IFLNK|0700, st_size=64, ...}) = 0
stat("/proc/3242/fd/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
openat(AT_FDCWD, "/proc/3242/fdinfo/0", O_RDONLY) = 7
...

따라서 lsof더 많은 파일을 읽으면 어떤 파일이 열려 있는지 알 수 있습니다! 구체적으로는 디렉토리 /proc/3242/fd. 아래의 모든 것은 /proc커널이 보관하는 "가짜" 파일 시스템입니다. ls -l구조를 보면 알 수 있습니다 .


파일 열기에 영향을 미침

파일 열기에 영향을 주는 데 사용할 수 있는 여러 가지 방법이 있지만 일부 스크립트를 바꾸는 것만큼 쉽지는 않습니다.

암호화 제공, 캐싱, 여러 디스크에 파일 분산 등 파일이 저장되거나 액세스되는 방식을 변경하려는 경우 이미 기존 방식이 있을 가능성이 높습니다.장치 매퍼귀하의 필요에 맞는 것입니다.

특정 디렉토리/마운트에서 파일 열기를 세밀하게 제어하려면 간단한 다음을 작성할 수 있습니다.퓨즈파일 시스템을 마운트하고 마운트하십시오.

프로그램/프로세스 수준에서 다음을 사용할 수 있습니다.LD_PRELOADC 라이브러리 호출을 변경하고 정상적인 syscall을 수행하지 못하도록 방지합니다.

가장 어렵지만 가장 유연한 방법은 자신만의 파일 시스템 드라이버를 작성하는 것입니다.

답변3

파일에 대한 액세스 관리는 운영 체제의 첫 번째이자 가장 중요한 기능에 관한 것입니다. 개인용 컴퓨터에서 가장 오래된 운영체제 중 하나인 DOS는 디스크 운영체제(Disk Operating System)를 의미한다. 이는 대부분의 경우 프로그램이 하드웨어에 직접 액세스할 수 있도록 허용했지만 파일 액세스에는 허용되지 않았습니다. 프로그램은 DOS 호출을 사용해야 했고 DOS는 프로그램의 파일 안팎으로 데이터를 넣는 작업을 관리했습니다. 디스크 유틸리티만 DOS에서 직접 하드 드라이브와 파일에 액세스할 수 있습니다.

Linux와 같은 최신 보호 모드 운영 체제는 DOS처럼 파일 액세스를 처리하지만 커널을 통과하려면 프로그램 자체(또는 메모리를 공유하도록 구성된 다른 프로그램) 외부의 모든 항목에 대한 모든 액세스가 필요합니다(Linux는 커널).

Linux의 프로그램은 C 라이브러리의 함수를 호출하여 파일에 데이터를 읽거나 쓸 수 있습니다. 그런 다음 C 라이브러리는 프로그램과 동일한 컨텍스트에서 계속 실행되는 동안 파일의 데이터에 대한 액세스를 구성하는 역할을 수행합니다. 그런 다음 C 라이브러리는 파일에 액세스하기 위해 올바른 함수를 사용하여 커널(Linux)을 호출하여 CPU를 링 0 또는 권한 모드로 전환합니다. 이제 CPU는 파일에 액세스하기 위해 하드웨어에 직접 액세스하는 권한 모드에서 Linux 파일 시스템 드라이버와 하드 드라이브 드라이버 소프트웨어를 실행하고 있습니다. 데이터는 C 라이브러리가 Linux에게 데이터를 넣으라고 지시한 메모리 영역에 복사되고, CPU는 프로그램의 보안 컨텍스트를 사용하여 사용자 모드로 다시 전환되고 C 라이브러리는 다시 시작되어 수행해야 하는 모든 처리를 수행합니다. 해당 데이터를 저장한 다음 프로그램 실행으로 돌아갑니다.

답변4

간단히 말해서, 프로그램이 파일에 쓸 때 이런 일이 발생합니다.

  1. open프로그램은 쓰기를 위해 경로로 지정된 파일을 커널에 요청합니다 .
  2. 커널은 일부 내부 구조를 설정하고 파일을 여는 작업 중 일부를 파일 시스템 유형에 맞는 드라이버에 위임합니다. 그런 다음 커널은 정수(예: 3)인 파일 설명자를 프로그램에 반환합니다.
  3. write프로그램은 파일 설명자가 참조하는 파일에 대한 일련의 바이트(예: 문자열)를 커널에 요청합니다 .
  4. 커널은 다시 드라이버에 작업을 위임합니다.
  5. 3단계와 4단계는 아마도 여러 번 반복될 것입니다.
  6. close프로그램은 파일 설명자가 참조하는 파일을 커널에 요청합니다 .
  7. 커널은 다시 작업을 드라이버에 위임한 다음 내부 구조를 파괴합니다.

다음은 "Hello World!"를 작성하는 아주 최소한의 어셈블리 프로그램입니다. Greeting.txt 파일에:

.text
.globl _start

_start:
    # Open and possible create file
    mov $2,             %rax        # syscall 'open'
    mov $path_start,    %rdi        # path
    mov $0101,          %rsi        # create + write
    mov $400,           %edx        # only user gets read permissions
    syscall

    mov %rax,           %r10        # file descriptor

    # Write string to file
    mov $1,             %rax        # syscall 'write'
    mov %r10,           %rdi        # file descriptor
    mov $msg_start,     %rsi        # start of data
    mov $msg_length,    %edx        # length of data
    syscall                         # perform syscall

    # Close file
    mov $3,             %rax        # syscall 'close'
    mov %r10,           %rdi        # file descriptor
    syscall

    # Exit program
    mov $60,            %rax        # syscall 'exit'
    syscall                         # perform syscall


.section .rodata

path_start:
    .string "greeting.txt\0"
path_end:
path_length = path_end - path_start


msg_start:
    .string "Hello World!\n"
msg_end:
msg_length = msg_end - msg_start

코드를 write.s에 저장하고 다음을 사용하여 빌드합니다.

as -o write.o write.s
ld -o write   write.o

그런 다음

./write

모든 것이 잘 작동되길 바랍니다.

(참고: 저는 어떤 오류 처리도 하지 않습니다. 이것은 단지 장난감 코드일 뿐입니다.)

관련 정보