8.14 代码:系统调用

通过使用底层提供的函数,大多数系统调用的实现都很简单(请参阅kernel/sysfile.c)。有几个调用值得仔细看看。

函数sys_linksys_unlink编辑目录,创建或删除索引节点的引用。它们是使用事务能力的另一个很好的例子。sys_linkkernel/sysfile.c:120)从获取其参数开始,两个字符串分别是oldnewkernel/sysfile.c:125)。假设old存在并且不是一个目录(kernel/sysfile.c:129-132),sys_link会增加其ip->nlink计数。然后sys_link调用nameiparent来查找newkernel/sysfile.c:145)的父目录和最终路径元素,并创建一个指向old的inode(kernel/sysfile.c:148)的新目录条目。new的父目录必须存在并且与现有inode位于同一设备上:inode编号在一个磁盘上只有唯一的含义。如果出现这样的错误,sys_link必须返回并减少ip->nlink

事务简化了实现,因为它需要更新多个磁盘块,但我们不必担心更新的顺序。他们要么全部成功,要么什么都不做。例如在没有事务的情况下,在创建一个链接之前更新ip->nlink会使文件系统暂时处于不安全状态,而在这两者之间发生的崩溃可能会造成严重破坏。对于事务,我们不必担心这一点

Sys_link为现有inode创建一个新名称。函数createkernel/sysfile.c:242)为新inode创建一个新名称。它是三个文件创建系统调用的泛化:带有O_CREATE标志的open生成一个新的普通文件,mkdir生成一个新目录,mkdev生成一个新的设备文件。与sys_link一样,create从调用nameiparent开始,以获取父目录的inode。然后调用dirlookup检查名称是否已经存在(kernel/sysfile.c:252)。如果名称确实存在,create的行为取决于它用于哪个系统调用:open的语义与mkdirmkdev不同。如果create是代表opentype == T_FILE)使用的,并且存在的名称本身是一个常规文件,那么open会将其视为成功,create也会这样做(kernel/sysfile.c:256)。否则,这是一个错误(kernel/sysfile.c:257-258)。如果名称不存在,create现在将使用iallockernel/sysfile.c:261)分配一个新的inode。如果新inode是目录,create将使用...条目对它进行初始化。最后,既然数据已正确初始化,create可以将其链接到父目录(kernel/sysfile.c:274)。Createsys_link一样,同时持有两个inode锁:ipdp。不存在死锁的可能性,因为索引结点ip是新分配的:系统中没有其他进程会持有ip的锁,然后尝试锁定dp

使用create,很容易实现sys_opensys_mkdirsys_mknodSys_openkernel/sysfile.c:287)是最复杂的,因为创建一个新文件只是它能做的一小部分。如果open被传递了O_CREATE标志,它将调用createkernel/sysfile.c:301)。否则,它将调用nameikernel/sysfile.c:307)。Create返回一个锁定的inode,但namei不锁定,因此sys_open必须锁定inode本身。这提供了一个方便的地方来检查目录是否仅为读取打开,而不是写入。假设inode是以某种方式获得的,sys_open分配一个文件和一个文件描述符(kernel/sysfile.c:325),然后填充该文件(kernel/sysfile.c:337-342)。请注意,没有其他进程可以访问部分初始化的文件,因为它仅位于当前进程的表中。

在我们还没有文件系统之前,第7章就研究了管道的实现。函数sys_pipe通过提供创建管道对的方法将该实现连接到文件系统。它的参数是一个指向两个整数的指针,它将在其中记录两个新的文件描述符。然后分配管道并安装文件描述符。