Linux的文件结构:
在Linux中,几乎一切都可以看做是文件来处理,文件是操作系统与具体物理设备之间的一个简单而统一的接口。所以,我们在程序中可以像使用文件那样去读写磁盘、操作串口、打印机等其他设备。常用的文件操作函数有,open、read、write、lseek。
那么内核如何区分和引用特定的文件呢?那就是通过
文件描述符
啦,文件描述符其实就是一些小值整数,我们可以通过这些整数传递给文件操作函数从而达到访问到具体哪一个文件。简单的说,文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现有的文件或者新建一个文件时,内核就向进程返回一个文件描述符。当需要读写文件时,就把文件描述符作为参数传递给相应的文件操作函数。
通常来说,当一个进程运行时,都会自动打开三个文件:标准输入(键盘)、标准输出(屏幕)、标准出错处理(屏幕)。这三个文件对应的文件描述符分别为0、1、2.(对应的宏为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO)。
系统调用:
所谓的系统调用其实就是操作系统提供给用户程序调用的一组“特殊的接口”,用户程序可以通过这个特殊的接口获得操作系统内核提供的服务。那么,我们就有疑问了?为啥操作系统不直接让用户直接访问内核,还搞出个“特殊的接口”,这不多此一举吗?非也非也,Linux操作在安全方面考虑的比较周到,为了更好的保护内核空间,将程序运行空间分为用户空间(0-3G)和内核空间(3G-4G),它们分别运行在不同的等级上,在逻辑上是相互分离的。通常情况系,用户进程在通常情况下不允许访问内核数据,也就不能访问内核函数,它们只能操作用户数据,调用用户函数。
但是,在有些情况下,用户空间进程需要获得一定的系统服务(调用内核的函数),这时操作系统就必须利用系统提供给用户的“特殊接口”——调用系统规定用户进程进入内核空间的具体位置。进入系统调用时,程序运行空间需要从用户空间进入内核空间,处理完之后再返回用户空间。
库函数:
在输入输出操作中,直接使用底层系统调用的问题是它们的效率非常低。为什么啊?
(1)与函数调用相比,系统调用的开销明显要大,因为在执行系统调用的时,Linux必须从用户态转换到内核态,还要返回用户态。多费劲呐~~减少这种开销的方法是,在程序中尽量减少调用次数(废话、这谁都知道),并且让每次系统调用完成尽可能多的工作(不要每次只写一个字,读一个字,多累啊)(我们最好像读磁盘数据一样,一次性大批量的读取它。哈哈~~)
(2)硬件会对底层系统调用一次所能读写的数据块做出一定的限制。
为了给设备和磁盘文件提供一个更高层更有效的接口,Linux提供了一系列的标准函数库。这些函数库提供输出缓冲功能的标准IO库就是这样一个例子。我们可以高效的写任意长度的数据块,函数则在数据满足数据长度要求时安排执行底层系统调用(通俗的讲就是,比如:邮递员告诉你,你有邮件了快来拿,然后你屁颠屁颠的去拿,拿了就回家。不久之后,他又打电话告诉你,你有邮件快来拿,然后你再屁颠屁颠的去拿,拿了又回去。再不久之后,他又来电话告诉你,你又有邮件了快来拿吧,结果你说,你个屌丝能不能一次性三个邮件一起拿给我啊?)带缓冲功能的标准IO库就是一次性给你三个邮件的好邮递员啦~~