rmdir과 unlink가 두 개의 별도 시스템 호출인 이유는 무엇입니까?

rmdir과 unlink가 두 개의 별도 시스템 호출인 이유는 무엇입니까?

잠시 동안 뭔가 혼란스러웠습니다.

[15:40:50][/tmp]$ mkdir a
[15:40:52][/tmp]$ strace rmdir a
execve("/usr/bin/rmdir", ["rmdir", "a"], [/* 78 vars */]) = 0
brk(0)                                  = 0x11bb000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff3772c3000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=245801, ...}) = 0
mmap(NULL, 245801, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff377286000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\36\3428<\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2100672, ...}) = 0
mmap(0x3c38e00000, 3924576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3c38e00000
mprotect(0x3c38fb4000, 2097152, PROT_NONE) = 0
mmap(0x3c391b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x3c391b4000
mmap(0x3c391ba000, 16992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3c391ba000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff377285000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff377283000
arch_prctl(ARCH_SET_FS, 0x7ff377283740) = 0
mprotect(0x609000, 4096, PROT_READ)     = 0
mprotect(0x3c391b4000, 16384, PROT_READ) = 0
mprotect(0x3c38c1f000, 4096, PROT_READ) = 0
munmap(0x7ff377286000, 245801)          = 0
brk(0)                                  = 0x11bb000
brk(0x11dc000)                          = 0x11dc000
brk(0)                                  = 0x11dc000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106070960, ...}) = 0
mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff370d5a000
close(3)                                = 0
rmdir("a")                              = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
[15:40:55][/tmp]$ touch a
[15:41:16][/tmp]$ strace rm a
execve("/usr/bin/rm", ["rm", "a"], [/* 78 vars */]) = 0
brk(0)                                  = 0xfa8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2388a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=245801, ...}) = 0
mmap(NULL, 245801, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3b2384d000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\36\3428<\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2100672, ...}) = 0
mmap(0x3c38e00000, 3924576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3c38e00000
mprotect(0x3c38fb4000, 2097152, PROT_NONE) = 0
mmap(0x3c391b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x3c391b4000
mmap(0x3c391ba000, 16992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3c391ba000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2384c000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2384a000
arch_prctl(ARCH_SET_FS, 0x7f3b2384a740) = 0
mprotect(0x60d000, 4096, PROT_READ)     = 0
mprotect(0x3c391b4000, 16384, PROT_READ) = 0
mprotect(0x3c38c1f000, 4096, PROT_READ) = 0
munmap(0x7f3b2384d000, 245801)          = 0
brk(0)                                  = 0xfa8000
brk(0xfc9000)                           = 0xfc9000
brk(0)                                  = 0xfc9000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106070960, ...}) = 0
mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3b1d321000
close(3)                                = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
geteuid()                               = 1000
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
faccessat(AT_FDCWD, "a", W_OK)          = 0
unlinkat(AT_FDCWD, "a", 0)              = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

디렉토리와 파일을 삭제하기 위해 별도의 시스템 호출이 있는 이유는 무엇입니까? 이 두 작업이 의미적으로 다른 이유는 무엇입니까?

답변1

디렉터리의 특별한 점은 디렉터리 내에서 여러 파일과 디렉터리를 참조할 수 있다는 것입니다. 따라서 상위 디렉터리를 삭제하면 해당 파일은 모두 액세스 가능한 참조 지점을 잃게 되며 프로세스도 마찬가지입니다. 이 경우에는 다음과 같은 rmdir()다양한 검사가 있습니다 unlink().

  • 디렉토리가 비어 있지 않은 경우. 디렉터리가 비어 있지 않으면 콘텐츠가 unlink제거/삭제될 때까지 콘텐츠를 삭제할 수 없습니다.

       ENOTEMPTY
          pathname contains entries other than . and .. ; or, pathname has
          ..  as its final component.  POSIX.1-2001 also allows EEXIST for
          this condition.
    
  • 디렉터리가 사용 중인 경우. 프로세스가 현재 디렉터리를 잃으면 문제가 발생하고 정의되지 않은 동작이 발생할 수 있습니다. 그들을 막는 것이 낫습니다.

       EBUSY  pathname  is currently in use by the system or some process that
          prevents its removal.  On Linux this means pathname is currently
          used  as  a  mount point or is the root directory of the calling
          process.
    

이러한 검사의 경우에는 unlink()존재하지 않습니다. 실제로 파일 이름을 삭제할 수 unlink()있으며 여전히 이를 사용/참조하는 프로세스에서는 문제 없이 파일 이름을 수정할 수 있습니다. 파일은 파일 설명자가 존재할 때까지 존재하며, 검색할 위치를 알지 않는 한 새 프로세스에서 액세스할 수 없습니다. 이는 *NIX 파일 시스템의 무지개색 손 마술의 일부입니다.

unlinkat()이제 둘 다에 대한 동작이 unlink()있거나 rmdir(2)원하는 경로에 따라 동작이 있습니다 .

답변2

내 개인 위키 페이지에서 복사함unlink("/") 및 rmdir("/")이 실패하는 이유는 무엇입니까?:

그러나 특별한 파일 형식이 있습니다.목차. 파일 시스템의 올바른 작동을 보장하려면 디렉토리에 대한 하드 링크를 일관된 방식으로 유지해야 합니다. 예를 들어 디렉토리를 생성하거나 삭제할 때 여러 하드 링크가 자동으로 생성되거나 삭제됩니다 /var/tmp/foo. 새 디렉터리(inode)를 참조하는 2개의 하드 링크가 다음 위치에 생성됩니다.

  • /var/tmp/foo– 귀하가 생성을 요청한 이름 자체
  • /var/tmp/foo/..이 새 디렉토리의 이름

또한 상위 디렉터리를 참조하는 또 다른 하드 링크가 생성되므로 /var/tmp/foo/..새로 생성된 디렉터리의 링크 수는 2가 됩니다.

이전 Unix 시스템에서는 디렉터리 하드 링크를 수동으로 조작할 수 있었습니다. 이는 주로 디렉터리 이름을 바꾸는 데 사용되었습니다.이름 바꾸기(2)그 당시에는 시스템 호출이 존재하지 않았습니다. 디렉터리 또는 다른 파일 형식의 파일 이름을 수동으로 바꾸려면 다음 명령을 사용하여 새 이름을 만듭니다.링크(2)을 누른 다음 다음을 사용하여 이전 이름을 삭제합니다.연결 해제(2);그러나 이 방법을 사용하여 디렉터리를 조작하는 것은 지나치게 강력한 경우가 많습니다. 사용자는 디렉터리를 부적절하게 연결하거나 연결 해제하여 파일 시스템에 쉽게 불일치를 초래할 수 있습니다. 예를 들어, 상위 디렉토리를 참조하지 않고 하드 링크를 생성하면 무한 디렉토리 루프가 발생하거나, 먼저 다른 위치에 링크를 생성하지 않고 디렉토리 하드 링크를 삭제하면 "해당 디렉토리 아래의 모든 파일이 나머지 파일과 연결이 끊어집니다." " 시스템 트리, 이러한 불일치가 발생할 것으로 예상됩니다.시스템 점검(8)다음 번 시스템 시작 시 문제가 올바르게 수정될 것이라는 보장은 없습니다.

유닉스 시스템에서 제공목차(2)이 시스템을 사용하여 기본 하드 링크뿐만 아니라 디렉토리, inode를 올바르게 제거하려면 하드 링크를 참조하는 통과 경로가 .또는 이 아니고 .., 하드 링크가 디렉토리 유형 inode를 참조하고 디렉토리가 비어 있어야 합니다. . 포함 ...링크만 호출에 성공하면 이 2개의 하드 링크, 경로에 지정된 디렉터리 하드 링크 및 디렉터리 inode가 삭제됩니다.

관련 정보