分享

UNIX文件系统的系统调用(五章)

 月影晓风 2014-05-03

      UNIX文件系统的系统调用(五章)

一、文件系统调用以及与其它算法的关系
 

二、系统调用open
 语法格式为:fd = open(pathname, flags, modes);算法如下:
输入--文件名,打开类型,文件许可权方式(以创建方式打开)
输出--文件描述符
{
 将文件名转换为索引节点(算法namei);
 if(文件不存在或不允许存取)
  return(错);
 为索引节点分配文件表项,置引用数和偏移量;
 分配用户文件描述符表项,将指针指向文件表项;
 if(打开的类型规定清文件)
  释放所有文件块(算法free);
 解锁(索引节点); //在上面的namei算法中上锁
 return(用户文件描述符);
}
 用户文件描述符的指针指向文件表,文件表的指针指向索引节点表。把文件表作为一个独立的数据结构来实现,是为了在若干用户文件描述符之间能够共享偏移量指针。前三个用户文件描述符分别叫做标准输入、标准输出和标准错误。

三、系统调用read
 语法格式:number = read(fd,buffer,count);算法如下:
输入--用户文件描述符,用户进程中的缓冲区地址,要读的字节数
输出--拷贝到用户区的字节数
{
 由用户文件描述符得到文件表项;
 检查文件的可存取性;
 在u区中设置用户地址,字节计数,输入/输出到用户的参数;
 从文件表中得到索引节点;
 索引节点上锁;
 用文件表中的偏移量设置u区中的字节偏移量;
 while(字节数不满足)
 {
  将文件偏移量转换为磁盘块号(算法bmap);
  计算块中的偏移量和要读的字节数;
  if(要读的字节数为0)
   break; //企图读文件尾
  读块(如果要超前读用算法breada,否则用算法bread);
  将数据从系统缓冲区拷贝到用户地址;
  修改u区中的文件字节偏移量域、读计数域,再写的用户空间地址;
  释放缓冲区; //在bread中上锁
 }
 解锁索引节点;
 修改文件表中的偏移量,用作下次读;
 return(已读的总字节数);
}
 当核心通过read循环时,它决定一个文件是否需要提前读:如果一个进程顺序地读两个块,核心就假定所有后继的读都将是顺序,直到证明不是顺序的。在循环的每次重复期间,核心将下一次逻辑块号保存在内存索引节点中,在下次循环期间,核心将当前逻辑块号和以前保存的逻辑块号相比较。如果它们相等,核心就为提前读计算物理块号并将其值保存在u区。算法breada将使用它。
 如果进程往文件写数据时引起的字节偏移从未对应到某些块上,那么索引节点中这些块登记项中保留着它们的初始值0。当一个进程企图从这样的块中读数据,核心将在read循环中分配任意一个缓冲区,将缓冲区的内容清为0,然后把该缓冲区拷贝到用户地址。

四、系统调用write
 写一个正规文件的算法和读一个正规文件的算法类似,然而,如果文件中还没有要写的字节偏移量所对应的块,核心要用算法alloc分配一个新块并将该块号存放在索引节点内容表中的正确位置上。如果这个字节偏移量是一个间接块中的偏移量,核心可能需要分配几个块,用作间接块和数据块。对应的索引节点在系统调用write期间是上锁的。因为核心在分配新块时可能会修改该索引节点。像在算法read中一样,核心在每次内层循环期间向磁盘写一块。在每次循环期间,核心要决定是写整个块还是只写块中的一部分。如果是后一种情况,核心必须先从磁盘上把该块读进来,以防止改写仍需保持不变的那些部分。但如果是写整块,核心就不必读该块。核心采取延迟写的方法将数据写到磁盘上,即先将其保存在高速缓存中。

五、文件和记录的上锁
 system V用系统调用fcntl(fd,cmd,arg)来实现文件和记录的锁操作,其中cmd指定锁操作的类型,arg规定各种参数。锁操作包括:①测试属于其他进程的锁并立即返回,指明是否发现其他的锁;②设置一个锁并进入睡眠直到成功;③设置一个锁,如果不成功,也立即返回。核心在关闭文件(close)时,自动释放由某个进程设置的锁。

六、文件的输入/输出位置的调整--lseek
 进程可以使用系统调用lseek来指定I/O的位置,从而实现文件的随机存取。lseek的语法格式为:position = lseek(fd,offset,reference)。其中offset是一个字节偏移量,reference指出该字节偏移量是从哪儿开始,position是返回值,即下次开始读/写的位置。
 为实现lseek,系统要做的只是调整文件表中的字节偏移量值。

七、系统调用close
 核心对文件描述符,对应的文件表祥和索引节点表象进行相应的处理来完成关闭文件的操作。如果文件表项的音用酥油用系统调用dup或fork的结果而大于1,那么就意味着还有其他用户文件描述符引用这个文件表项。这时,核心将引用数减1,关闭操作就完成了。如果文件表项的引用数为1,核心则释放该表项,使它重新可用,并释放在系统调用open时分配的内存索引节点(算法iput)。如果其它进程还引用该索引节点,核心则使索引节点引用数减1,并仍然保持它和其他进程的联系;否则,核心归还该索引节点以便再次分配。当一个进程退出时,核心检查它的活动的用户文件描述符并在内部关闭它们。

八、文件系统的建立
 系统调用creat的语法格式为:fd = creat(pathname,modes);算法为:
输入--文件名、许可权方式
输出--文件描述符
{
 取对应文件名的索引节点(算法namei);
 if(文件已经存在)
 {
  if(不允许访问)
  {
   释放索引节点(iput);
   return(错);
  }
 }
 else //文件还不存在
 {
  从文件系统中分配一个空闲索引节点(算法ialloc);
  在父目录中建立新目录项:包括新文件名和新分配的索引节点号;
 }
 为索引节点分配文件表项,初始化引用数;
 if(文件在创建时已存在)
  释放所有文件块(算法free);
 解锁(索引节点);
 return(用户文件描述符);
}
 核心在建立文件时先为其分配索引节点,再将目录写到磁盘上是为了在这两个事件之前若发生意外将少做一些恢复工作。这时文件系统中将存在一个不被任何路径引用的索引节点而不是一个引用错误索引节点的路径名。

九、特殊文件的建立
 系统调用mknod用来在文件系统中建立一些特殊文件,包括有名管道、设备文件和目录。语法格式为:mknod(pathname,type and permissions,dev);算法为:
输入--节点(文件名),文件类型,许可权方式,主设备号和次设备号
输出--无
{
 if(新节点不是有名管道而且用户不是超级用户)
  return(错);
 取新节点的父索引节点(算法namei);
 if(新节点已经存在)
 {
  释放父索引节点(算法iput);
  return(错);
 }
 从文件系统中为新节点分配一个自由索引节点(算法ialloc);
 在父目录中建立新目录表项:包括新节点名和新分配的索引节点号;
 释放父目录的索引节点(算法iput);
 if(新节点为块或字符特殊文件)
  将主、次设备号写入索引节点结构;
 释放新索引节点(算法iput);
}

十、改变目录及根
 算法chdir改变一个进程的当前目录,语法格式为:chdir(pathname);算法为:
输入--新目录名
输出--无
{
 取新目录名的索引节点(算法namei);
 if(索引节点不是目录的索引节点或不允许进程存取该文件)
 {
  释放该索引节点(算法iput);
  return(错);
 }
 解锁索引节点;
 释放“老”的当前目录的索引节点(算法iput);
 将新索引节点放到u区中当前目录域中;
}
 算法chroot改变一个进程的文件系统逻辑根,其算法基本上和算法chdir相同。然而核心的缺省根存放在全局变量中,核心并不自动地释放老的根的索引节点。

十一、改变所有者及许可权方式
 改变一个文件的所有者或许可权方式是对文件的索引节点的操作而不是文件本身。这两个系统调用的格式为:
  chown(pathname, owner, group);
  chmod(pathname, mode);
 核心首先通过算法namei将文件名转换为索引节点然后判断进程所有者的权限,若是超级用户或者该索引节点的所有者则执行修改索引节点操作再将修改结果保存到磁盘上(算法iput);

十二、系统调用stat和fstat
 系统调用stat和fstat允许进程查询文件的状态,它们返回诸如文件类型、文件所有者、存取许可权、文件大小、联结的数目、索引节点号、文件的访问时间等信息。语法格式为:
  stat(pathname, statbuffer);
  fstat(fd, statbuffer);
 这两个系统调用只是简单地将对应的索引节点中的若干域写到statbuffer中。

十三、管道
 管道的实现使进程之间能够通信,管道的传统实现方法是采用文件系统作为数据存储。有两种类型的管道:有名管道和无名管道。除了进程最初存取它们的方式不同外,这两种管道是一样的。进程对有名管道使用open,但使用系统调用pipe来建立无名管道,然后在使用管道时,进程用正规文件的系统调用,如read,write和close。只有发出pipe调用的进程及其后代可以使用无名管道,但所有进程都可以使用有名管道。
 系统调用pipe创建无名管道,语法格式为:pipe(fdptr);这里fdptr是指向一个整形数组的指针,这个整形数组将含有读、写管道用的两个文件描述符。算法为:
输入--无,
输出--读文件描述符和写文件描述符
{
 由管道设备中分配一个新索引节点(算法ialloc);
 分配用于读和写的两个文件表项;
 初始化文件表项,使它们指向新索引节点;
 分配用于读和写的两个用户文件描述符;
 初始化用户文件表项,使它们分别指向对应的文件表项;
 将索引节点引用数置为2;
 将索引节点读计数和写计数分别置为1;
}
 管道设备就是一个文件系统,核心能够从中为管道分配索引节点和数据块。在系统生成时,系统管理员要指定管道设备,管道的字节偏移量保存在索引节点中,用lseek不能改变。
 有名管道的打开与打开一个正规文件的算法基本相同,然而在完成系统调用之前,核心增加索引节点中的读计数或写计数。为读而打开有名管道的进程将进入睡眠,直到另一进程以写方式打开该管道,反之亦然。
 核心存取管道数据的方式和它存取正规文件的数据的方式完全一样。为管道分配存储空间和为正规文件分配存储空间的不同之处是,管道只使用索引节点的直接块以获得较高的效率。核心将索引节点的直接块做为循环队列来管理。当进程读管道时首先检查管道是否为空,读结束时核心唤醒所有睡眠的写进程并将读偏移量保存在索引节点中。如果一个进程写管道时管道慢,核心将对该索引节点做标记,然后,进程睡眠。
 关闭管道的算法和关闭正规文件的一样,只不过核心在释放管道的索引节点之前要做一些特殊处理。如果没有读进程或写进程存取管道,核心则释放它的所有数据块并修改索引节点来指明管道为空。当核心释放一个无名管道时,它释放读索引节点的磁盘拷贝。

十四、系统调用dup
 系统调用dup将一个文件描述符复制到该用户文件描述符表中的第一空槽中,实现文件描述符的重定位,语法格式为:newfd = dup(fd);

十五、文件系统的安装和拆卸
 磁盘的一个段可以含有一个逻辑的文件系统,由自举块、超级块、索引节点和数据块组成。系统调用mount将一个文件系统连到另一个文件系统目录树中,而系统调用umount将一个文件系统以该文件系统目录树中拆卸下来,mount的语法格式为:
 mount(special pathname, directory pathname, options);
进程可以在被安装的支持系统上存取文件,但不允许link扩展到多个系统,核心有个安装表,每个被安装的文件系统在该表中都占有一个表项。每个安装表项含有如下的域:⑴设备号,用来标识被安装的文件系统,(即前述的逻辑文件系统号);⑵指向含有被安装文件系统超级块的缓冲区的指针;⑶指向被安装的文件系统的根索引节点的指针;⑷指向安装点的目录的索引节点的指针。mount算法如下:
输入--块特殊文件的文件名,安装点的目录名,选择项(只读)
输出--无
{
 if(非超级用户)
  return 错;
 取块特殊文件的索引节点(算法namei);
 作合法性检查;
 取安装点目录名的索引节点(算法namei);
 if(不是目录,或引用数大于1)
 {
  释放索引节点(算法iput);
  return 错;
 }
 查找安装表中的空槽; //标记这个槽位使用,同时为设备号赋值。
 调用块设备驱动程序的open子程序;
 从高速缓存中取空闲缓冲区;
 将超级块读入空闲缓冲区;
 初设超级块中的各域;//清除自由块链表的锁域和自由索引节点的锁域并将超级块中的自由索引节点数置零。
 取被安装设备的根索引节点,并保存在安装表中;
 标记安装点的目录索引节点为安装点;
 释放特殊文件的索引节点(算法iput);
 解锁安装点目录的索引节点;
}
 安装一个文件系统后,算法namei、iget要作如下修改:
算法iget:
输入--文件系统的索引节点号
输出--上锁的索引节点
{
 while(没做完)
 {
  if(索引节点在索引节点高速缓存中)
  {
   if(索引节点被锁)
   {
    sleep(索引节点解锁事件);
    continue;
   }
   //对安装点的特殊处理
   if(索引节点是安装点)
   {
    查安装表,找安装点;
    从安装表中取新文件系统号;
    在查找中用新的根索引节点号;
    continue;
   }
   if(索引节点在索引节点空闲链表中)
    从空闲链表中取出索引节点;
   索引节点引用数加1;
   return (索引节点);
  }
  //索引节点不在索引节点高速缓存中
  从空闲链表中取新索引节点;
  设置索引节点号和文件系统;
  从老的杂凑队列中取走索引节点,放入新的索引节点;
  从磁盘读索引节点(算法bread);
  初始化索引节点(如,置引用数为1);
  return (索引节点);
 }
}
算法namei:
输入--路径名;
输出--上锁的索引节点
{
 if(路径名从根开始)
  工作索引节点 = 根索引节点(算法iget);
 else
  工作索引节点 = 当前目录的索引节点(算法iget);
 while(还有路径名分量)
 {
  从输入中读下一个路径名分量;
  验证索引节点是目录,验证许可权;
  if(索引节点是改变了的根目录而且成分名为“..”);
   continue;
component search: //查找分量
  读索引节点(目录)(算法bmap,bread,brelse);
  if(路径名分量与一个目录表项匹配)
  {
   取被匹配的分量的索引节点号;
   if(找到的索引节点是根、工作索引节点是根而且分量名为“..”)
   {
    //跨越安装点
    取工作索引节点的安装表表项;
    释放工作索引节点(算法iput);
    工作索引节点 = 安装点索引节点;
    锁安装点的索引节点;
    工作索引节点引用数加1;
    goto component search(找“..”);
   }
   释放工作索引节点(算法iput);
   工作索引节点 = 新索引节点号的索引节点(算法iget);
  }
  else
   return (错);
 }
 return (工作索引节点);
}
 系统调用umount拆卸文件系统,语法格式为:
  umount(special filename);
算法为:
输入--要拆卸的文件系统的特殊文件名
输出--无
{
 if(不是超级用户)
  return (错);
 取特殊文件的索引节点(算法namei);
 取出要被拆卸的主、次设备号;
 根据主、次设备号取要拆卸的文件系统的安装表项;
 释放特殊文件的索引节点(算法iput);
 从区表中清除属于该文件系统的共享正文表项;
 更新超级块、索引节点、腾空缓冲区;
 if(该文件系统中仍有正在使用的文件)
  return (错);
 从安装表中取要拆卸的文件系统的根索引节点;
 锁该索引节点;
 释放该索引节点(算法iput); //在mount中是iget
 调用该特殊设备的关闭子程序;
 使被拆卸的文件系统在缓冲池中的缓冲区无效;
 从安装表中取安装点的索引节点;
 锁该索引节点;
 清除其安装点标志;
 释放该索引节点(算法iput); //在mount中是iget
 释放超级块所用的缓冲区;
 释放安装表项;
}

十六、系统调用link
 link为一个已存在的索引节点创建一个新的目录项,语法格式为:
  link(source file name, target file name);
其算法为:
输入--已存在的文件名、新文件名
输出--无
{
 取已存在的文件的索引节点;
 if(文件的连接数过多或无超级用户许可权而连接目录)
 {
  释放索引节点(算法iput);
  return (错);
 }
 索引节点的联结数加1;
 修改索引节点的磁盘拷贝;
 解锁索引节点;
 取将含有新文件名的父目录的索引节点(算法namei);
 if(新文件名已存在或已存在的文件与新文件在不同的文件系统中)
 {
  使上述更新操作作废;
  return (错);
 }
 在新文件名的父目录中建立新目录表项;
 填入新文件名、源文件名的索引节点号;
 释放父目录的索引节点(算法iput);
 释放源文件的索引节点(算法iput);
}

十七、系统调用unlink
 unlink清除文件的一个目录表项,该调用的语法格式是:
  unlink(pathname);
算法为:
输入--文件名
输出--无
{
 取要拆除的文件的父索引节点(算法namei);
 //如果拆除当前目录
 if(文件名的最后分量是“.”)
  父索引节点的引用数加1;
 else
  取要被拆除的文件的索引节点(算法iget);
 if(拆除的文件是目录,但用户不是超级用户)
 {
  释放索引节点(算法iput);
  return (错);
 }
 if(是共享正文文件,而且联结数为1)
  从区表中清除;
 写父目录:将拆除文件的索引节点号至0;
 释放父目录的索引节点(算法iput);
 //iput检查联结数是否为零,如果是释放文件块(算法free)
 //和归还索引节点(算法ifree);
}

十八、文件系统的一致性
 为了当系统发生故障时,使文件系统的破坏达到最小程度,核心要按照某种次序来进行写磁盘操作。同理,核心也应按特别的次序释放索引节点和磁盘块,来减少系统发生故障时所造成的破坏。
 在系统调用unlink中,存在许多竞争条件,特别是在拆除目录时,一个进程可能在另一个进程进行rmdir确定该目录为空之后又在该目录中创建一个文件。用户只有靠利用文件和记录上锁功能,才能避免这种情况。另一种竞争条件发生在:一个进程正在用算法namei将一个文件的路径名转变为一个索引节点时,另一个进程正在清除该路径名中的一个目录。可能发生的最坏情况是,进程取错了文件,因而可能破坏了保密性--但是,这种竞争条件在实际中很少发生。
 当一个进程使一个文件处于打开状态时,另一个进程可能在该文件打开期间拆除该文件。打开文件的进程仍能用它的文件描述符进行正常的文件操作,但是当它关闭该文件时,核心便清除该文件内容。利用这个特点,一些进程通常创建一些临时文件并立即拆除它们,如果这些进程失败不会留下临时文件的痕迹。

十九、文件系统的抽象
 文件系统类型允许核心同时支持多种文件系统,进程用通常的unix系统调用存取这些文件,核心再将文件操作的通用集映射到每个文件系统中专用操作。一个通用的内存索引节点中所含的数据与特定的文件系统无关,它指向一个文件系统专用的索引节点。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多