# 系統程式設計 - `open()`, `read()`, `write()` [TOC] ## 課程影片 ### 系統程式設計 W2 - 3 {%youtube gXuD52Mdvho %} [`man 2 open`](https://man7.org/linux/man-pages/man2/open.2.html) ## 冷知識 ### 追蹤 `open(2)` 的流程 如果用 `hole.c` 去追蹤 `open()` 的流程,會出現下面的結果: ```c 1d... 0.797 us | post_ttbr_update_workaround(); 1d... | do_el0_svc() { 1d... | el0_svc_common.constprop.0() { 1.... | invoke_syscall() { 1.... | __arm64_sys_openat() { 1.... | do_sys_openat2() { 1.... | getname() { 1.... + 11.833 us | getname_flags.part.0(); 1.... + 15.981 us | } 1.... | get_unused_fd_flags() { 1.... 3.314 us | alloc_fd(); 1.... 7.426 us | } 1.... | do_filp_open() { 1.... ! 286.666 us | path_openat(); 1.... ! 292.204 us | } 1.... 2.092 us | __fsnotify_parent(); 1.... 2.130 us | fd_install(); 1.... | putname() { 1.... 2.203 us | kmem_cache_free(); 1.... 6.240 us | } 1.... ! 340.685 us | } 1.... ! 344.814 us | } 1.... ! 348.889 us | } 1d... ! 353.260 us | } 1d... ! 354.667 us | } ``` ### 那個 Open File Table 從上面的追蹤結果可以推論出影片中的每個行程自己維護的 *Table of Opened Files (Per Process)* 是在 `struct task_struct` 中的 `struct files_struct *files` 成員: ```c struct task_struct { ... /* Filesystem information: */ struct fs_struct *fs; /* Open file information: */ struct files_struct *files; ... }; ``` 其中,那個 `struct files_struct` 的定義為: ```c /* * Open file table structure */ struct files_struct { /* * read mostly part */ atomic_t count; bool resize_in_progress; wait_queue_head_t resize_wait; struct fdtable __rcu *fdt; struct fdtable fdtab; /* * written part on a separate cache line in SMP */ spinlock_t file_lock ____cacheline_aligned_in_smp; unsigned int next_fd; unsigned long close_on_exec_init[1]; unsigned long open_fds_init[1]; unsigned long full_fds_bits_init[1]; struct file __rcu * fd_array[NR_OPEN_DEFAULT]; }; ``` 而如果要從 `fd` 查到圖片中所說的 *System Open File Table* 中記錄的檔案,那要查這個 `struct files_struct *files` 中的 `struct fdtable *fdt` 中的 `struct file **fd`。其中,`struct fdtable` 的定義如下: ```c struct fdtable { unsigned int max_fds; struct file __rcu **fd; /* current fd array */ unsigned long *close_on_exec; unsigned long *open_fds; unsigned long *full_fds_bits; struct rcu_head rcu; }; ``` 而最後找到的「那個 *System Open File Table*」裡面的東西,就是 `struct file`: ```c struct file { union { struct llist_node fu_llist; struct rcu_head fu_rcuhead; } f_u; struct path f_path; struct inode *f_inode; /* cached value */ const struct file_operations *f_op; /* * Protects f_ep, f_flags. * Must not be taken from IRQ context. */ spinlock_t f_lock; enum rw_hint f_write_hint; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; struct mutex f_pos_lock; loff_t f_pos; struct fown_struct f_owner; const struct cred *f_cred; struct file_ra_state f_ra; u64 f_version; #ifdef CONFIG_SECURITY void *f_security; #endif /* needed for tty driver, and maybe others */ void *private_data; #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct hlist_head *f_ep; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; errseq_t f_wb_err; errseq_t f_sb_err; /* for syncfs */ } ``` 這個是在 `do_sys_openat2()` 最後階段的 `fd_install()` 中推論出來的。因為這個函式的功能是在 `fd` 對應到「打開的檔案」的那個陣列,把這個陣列的第 `fd` 個元素改成「那個代表已開啟檔案」的資料結構,也就是裡面的 `rcu_assign_pointer(fdt->fd[fd], file)` 。從這個敘述一路反推,就可以知道開啟檔案而有新的 `fd` 時,會對應到行程的哪個資料結構: ```c void fd_install(unsigned int fd, struct file *file) { struct files_struct *files = current->files; struct fdtable *fdt; rcu_read_lock_sched(); ... /* coupled with smp_wmb() in expand_fdtable() */ smp_rmb(); fdt = rcu_dereference_sched(files->fdt); BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); rcu_read_unlock_sched(); } ``` 從這當中可以發現:那個「以 `fd` 為 key,去查到檔案」的流程是 `current->files->fdt->fd[fd]`。所以就可以得到前面的推論。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up