Linux
為什麼 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 +++
為什麼有單獨的系統呼叫來刪除目錄和文件?為什麼這兩個操作在語義上是不同的?
目錄是特殊的,因為在一個目錄中您可以引用多個文件和目錄,因此,如果您刪除父目錄,所有這些文件都會失去它們可以訪問的引用點,與程序相同。對於這種情況,
rmdir()
有不同的檢查,不同於unlink()
:
- 如果目錄不為空。
unlink
如果目錄不為空,則在內容被“d/removed”之前無法將其刪除。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)
取決於您期望的路徑。