2021SC@SDUSC
Now we've known what fd is, how does fd function remains unseen.Start with function open(), we shall discuss about how actually those operations to files finally happens.
Open()We might write some code to open a file in linux that is:
#includeusing namespace std; int main() { off_t fd; fd=open("rd.in",O_RDONLY); cout< fd will be given an number which is unsigned integer that we've talked about.Usually the number is larger than 3 because 0,1,2 have been used as stdin, stdout and stderr.
Do_sys_openHow does the kernal get such fd?Go back to functions to see.When users' process call open() to open a file, kernal VFS will call sys_open()->do_sys_open() to function.Here's part of the code.
long do_sys_open(int dfd, const char __user *filename, int flags, int mode) { char *tmp = getname(filename); //copy filename and other messages to the kernal space. int fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { fd = get_unused_fd_flags(flags); //get an unused fd flag. if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, flags, mode); //new file struct if (IS_ERR(f)) { put_unused_fd(fd); //if err, release fd for others. fd = PTR_ERR(f); } else { fsnotify_open(f->f_path.dentry); fd_install(fd, f); // install the file(use fd)into the fd list of the processes. } } putname(tmp); } return fd; //that is the fd we needed. }Filename(route) is copied to the kernal space, and with the route the kernal manage to find the dentry in the file system through which we could find the inode which point at the operations towards the file.Although through we could check the inode for each file, the user's process will only need to know fd to open the file and read the inside data.
Do_filp_openThen the function do_flip_open initialize a struct file, which also is a bridge between file and process.This function also finished most of the word in opening a file.This function is defined in fs/namei.c
struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { struct nameidata nd; int flags = op->lookup_flags; struct file *filp; set_nameidata(&nd, dfd, pathname, NULL); filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) filp = path_openat(&nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(&nd, op, flags | LOOKUP_Reval); restore_nameidata(); return filp; }The lower 2 bits in parameter open_flag show the permission to the file, which means:
00 - read-only
01 - write-only
10 - read-only
11 - specialThe permission will soon be saved to flag.
Now we mainly care about how the pathname is caculated.In function path_init, nd(nameidata, a pointer to struct nameidata) will be filled.So what will be done to fill the nd?Let's dive into the function path_openat which turns out to be the most necessary part.Well firstly what function unlikely and likely actually mean?
Likely and unlikely
Unlikely() and likely() is macros defined in include/linux/compiler.h:
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)Note:You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c.
The explaination means that something is 'likely' to happen, which means the if case often occurs.Unlikely(), of course, means the opposite way that is the else case is most likely to happen.So if likely() appears, the cpu will compile the if case forward to increase the efficiency.
Unlikely() and likely() will not effect the correctness of the program, but will fastern the running speed.
So as we see the code above, we could say that filp==ERR_PTR(_ECHILD) is not likely to happen.
nameidata(nd)nameidata is a struct to save the context and result in path_walking procedure.
struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; unsigned int flags; unsigned seq; int last_type; unsigned depth; char *saved_names[MAX_NESTED_linkS + 1]; };The next blog we'll analyse how path_openat works and the role nd played in it.



