ทำไม rmdir และยกเลิกการเชื่อมโยงการเรียกระบบสองครั้งแยกกัน?


10

นี่คือสิ่งที่ทำให้ฉันสงสัยอยู่พักหนึ่ง:

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

เหตุใดจึงมีการเรียกระบบแยกต่างหากสำหรับการลบไดเรกทอรีและไฟล์ ทำไมการดำเนินการทั้งสองนี้จึงมีความแตกต่างทางความหมาย


3
ตอบแล้วที่นี่: superuser.com/questions/430313/…
jlliagre

คำตอบ:


9

ไดเรกทอรีมีความพิเศษในแง่ที่ว่าคุณสามารถมีการอ้างอิงไปยังหลาย ๆ ไฟล์และไดเรกทอรีได้ดังนั้นหากคุณลบไดเรกทอรีหลักไฟล์เหล่านั้นทั้งหมดจะสูญเสียจุดอ้างอิงจากตำแหน่งที่พวกเขาสามารถเข้าถึงได้เช่นเดียวกันกับกระบวนการ สำหรับกรณีดังกล่าวrmdir()มีการตรวจสอบที่แตกต่างกันซึ่งแตกต่างจากunlink():

  • หากไดเรกทอรีไม่ว่างเปล่า หากไดเรกทอรีไม่ว่างเปล่าจะไม่สามารถลบออกได้จนกว่าเนื้อหาจะunlink'd / ถูกลบ

       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()และกระบวนการที่ยังคงใช้ / ทำการอ้างอิงถึงมันสามารถแก้ไขได้โดยไม่มีปัญหา ไฟล์นั้นมีอยู่จนกว่าไฟล์ descriptor จะมีอยู่ แต่ไม่สามารถเข้าถึงกระบวนการใหม่ได้ (เว้นแต่คุณจะทราบตำแหน่งที่จะค้นหา) นี่เป็นส่วนหนึ่งของเวทย์มนตร์มือสีรุ้งของระบบไฟล์ * NIX

ขณะนี้มีสิ่งunlinkat()ที่ทำหน้าที่เป็นทั้งสองอย่างunlink()หรือrmdir(2)ขึ้นอยู่กับเส้นทางซึ่งเป็นสิ่งที่คุณคาดหวัง


rm -rf "$PWD"ทำงานได้ดีและลบไดเรกทอรีปัจจุบัน ฉันคิดว่าเหตุผลที่มีrmdir()อาจเป็นประวัติศาสตร์ (เริ่มแรกไดเรกทอรีถูกยกเลิกการเชื่อมโยง () และ rmdir (คำสั่ง) ถูกยกเลิกการเชื่อมโยง dir /., dir / .. และ dir และเมื่อนั้นถูกย้ายไปยังเคอร์เนลที่จะต้อง syscall ใหม่ทำทั้ง 3 อย่างน้อยสำหรับระยะเวลาการเปลี่ยนแปลงหรือสิ่งที่ต้องการ)
Stéphane Chazelas

@ StéphaneChazelasเห็นด้วยนั่นคือเหตุผลที่ฉันเพิ่ม unlinkat
Braiam

ถ้าฉันอ่านคำตอบของคุณอย่างถูกต้องคุณกำลังพูดrmdir(dir)ว่าไม่ได้ทำงานหากdirใช้งานอยู่ ที่ไม่เป็นความจริงบน Linux อย่างน้อยก็ใช้rmdir(getcwd())งานได้ดี (ให้ไดเรกทอรีปัจจุบันว่างเปล่า)
Stéphane Chazelas

@ StéphaneChazelasแก้ไขโดยใช้กระบวนการหรือเป็นจุดเมานท์: EBUSY พา ธ ที่ ขณะนี้อยู่ในการใช้งานโดยระบบหรือกระบวนการบางอย่างที่จะช่วยป้องกันการกำจัดของมัน บน Linux หมายถึงชื่อพา ธ ที่ใช้เป็นจุดเมานท์หรือเป็นไดเรกทอรีรากของกระบวนการเรียก
Braiam

ผมไม่แน่ใจว่าสิ่งที่พวกเขาหมายถึงหรือเป็นไดเรกทอรีรากของกระบวนการเรียก mkdir test; sudo strace -e chroot,rmdir perl -e 'chroot("test"); rmdir("test")'แสดงทั้ง chroot และ rmdir สำเร็จ
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.