Tại sao rmdir và hủy liên kết hai cuộc gọi hệ thống riêng biệt?


10

Đây là một cái gì đó khiến tôi băn khoăn một lúc:

[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 +++

Tại sao có các cuộc gọi hệ thống riêng biệt để xóa một thư mục và tập tin? Tại sao hai hoạt động này sẽ khác biệt về ngữ nghĩa?


3
Đã trả lời tại đây: superuser.com/questions/430313/ khăn
jlliagre

Câu trả lời:


9

Các thư mục đặc biệt theo nghĩa là trong một thư mục bạn có thể có các tham chiếu đến một số tệp và thư mục, vì vậy, nếu bạn xóa thư mục mẹ, tất cả các tệp đó sẽ mất điểm tham chiếu từ nơi chúng có thể được truy cập, cùng quy trình. Đối với những trường hợp như vậy, rmdir()có các kiểm tra khác nhau, khác với unlink():

  • Nếu thư mục không trống. Nếu một thư mục không trống, nó không thể xóa nó cho đến khi nội dung được unlink'd / xóa.

       ENOTEMPTY
          pathname contains entries other than . and .. ; or, pathname has
          ..  as its final component.  POSIX.1-2001 also allows EEXIST for
          this condition.
    
  • Nếu thư mục được sử dụng. Nếu một quá trình làm mất thư mục hiện tại của họ, nó có thể dẫn đến các vấn đề và hành vi không xác định. Là tốt hơn để ngăn chặn chúng.

       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.
    

Trong trường hợp unlink()những kiểm tra này không tồn tại. Trong thực tế, bạn có thể xóa tên của một tệp unlink()và quá trình vẫn đang sử dụng / tạo tham chiếu đến nó, có thể sửa đổi nó mà không gặp vấn đề gì. Tệp tồn tại cho đến khi mô tả tệp tồn tại, chỉ không thể truy cập vào quy trình mới (trừ khi bạn biết nơi tìm kiếm). Đây là một phần của phép thuật màu cầu vồng của các hệ thống tệp * NIX.

Bây giờ, có những unlinkat()hành vi như cả hai, unlink()hoặc rmdir(2)tùy thuộc vào con đường mà bạn mong đợi.


Cũng rm -rf "$PWD"hoạt động và không loại bỏ các thư mục hiện tại. Tôi nghĩ rằng lý do có rmdir()lẽ là lịch sử (ban đầu, các thư mục không được liên kết () và rmdir (lệnh) đã hủy liên kết dir /., Dir / .. và dir, và khi đó đã được chuyển đến kernel, đó phải là một tòa nhà mới làm cả 3 ít nhất là trong một giai đoạn chuyển tiếp hoặc đại loại như thế)
Stéphane Chazelas

@ StéphaneChazelas đồng ý, đó là lý do tại sao tôi đã thêm unlinkat.
Braiam

Nếu tôi đọc đúng câu trả lời của bạn, bạn đang nói rmdir(dir)không hoạt động nếu dirđang sử dụng. Điều đó không đúng với Linux ít nhất, nơi rmdir(getcwd())hoạt động tốt (với điều kiện thư mục hiện tại trống).
Stéphane Chazelas

@ StéphaneChazelas chính xác, được sử dụng bởi một quy trình hoặc như điểm gắn kết: EBUSY tên đường dẫn hiện đang được sử dụng bởi hệ thống hoặc một số quy trình ngăn chặn loại bỏ nó . Trên Linux, điều này có nghĩa là tên đường dẫn hiện đang được sử dụng làm điểm gắn kết hoặc là thư mục gốc của quá trình gọi.
Braiam

Tôi không chắc ý nghĩa của chúng là gì hoặc là thư mục gốc của quy trình gọi . mkdir test; sudo strace -e chroot,rmdir perl -e 'chroot("test"); rmdir("test")'cho thấy cả chroot và rmdir thành công.
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.