linux系统调用和文件操作
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
零、本课程能学到的内容
1、文件系统及文件的IO操作。
2、linux的多进程编程。
3、linux的多线程编程。
4、进程及线程之间的同步和异步通信
5、linux的网络编程(编写服务器、客户端、TCP、UDP程序)。
一、linux的系统调用
用户程序通过软中断的方式,让cpu切换到内核态运行,数据就借此传输。
对于CPU来说,它不知道现在运行是用户程序还是内核程序。软中断后,cpu根据拿到的数据,进行特定的任务,然后退出中断,将任务的结果交回给用户程序。
这个任务如何描述?是通过数字来编号的。
例子:写一个打印字符串的程序,通过系统调用来完成。
需要传递的参数有:指向字符串的指针(字符串的首地址),字符串的长度。
汇编写法
用int$0x80产生软中断
C的写法
直接使用syscall函数(它的本质就是用软中断来产生系统调用)
系统调用编号见:/usr/include/asm/unistd_32.h里
一共有358个。其中,4号对应的write调用。
二、linux文件系统基础
扇区(sector):磁头读取的最小单位数据,在磁盘上类似一个扇子的形状。一般是512个字节。
块(block):软件读取磁盘的最小单位数据,一般来说是扇区的2的n次幂大小,常用的有4096个字节。
块就是文件存放的最小单位。如果有一个文件是15045个字节,那么就分成4个块存储,最后那个块的数据是不满的。
可以用数组的方式将一个文件的所有数据块的编号保存起来。
这个数组多大合适?
前提是数组大小要固定,才好管理。
用一个15元素的数组来保存数据块的编号。
其中0-11元素,保存数据块的前面12个,第12个元素是一个一级指针,指向另一个表格,那个表格是4096字节,可以保存1024个块的编号。
如果还不够,第13个元素是一个二级指针,指向的那个表格是一级指针表格。14个元素是三级指针,指向的表格是二级指针表格。
全部用完可以存储2G大小的文件的数据块编号。
在文件系统内部,如何来表示一个文件呢?
用一个inode结构体来表示一个文件。inode是index node的意思。也叫索引节点。一个索引节点对应一个文件,有多少个文件就有多少个索引节点。
inode结构体里面有文件的大小、文件的修改时间、文件的权限、文件的数据块的编号数组。
inode结构体数组保存了所有文件的inode,这个数组的下标就是inode编号。文件名或者目录名保存目录文件的数据块里面。在linux里面,一个目录也是一个文件,叫做目录文件,其它的叫做普通文件。目录文件里就有,inode编号和文件名的对应关系。
那么,给出文件路径时,如何找到这个文件的数据块?
/usr/include/stdio.h
首先,找到根目录的数据块,从里面找到文件和inode编号关系表,
例如usr这个文件名和inode编号为10002是对应的,那么就到inode结构体数组里找出下标为10002的元素,得到对应文件usr的数据块,因为usr也是一个文件夹,既然是文件夹,它的数据块里面也会有关系表,就可以找到include这个文件的数据块,最后找到stdio.h的数据块,这个是一个普通文件,就把它的数据块全部取出来。
这里搜索的起点是根目录的数据块,那么这个文件的数据块到哪里找?
在一个磁盘里面,最开始的几个块,存储了本磁盘的很多重要数据,例如块的大小,有哪些inode下标空闲,哪些已经用了,最重要的是根目录的数据块。
所有的参数存放在其中的一个块,这个块叫做超级块(super block)。在磁盘格式化,实际就是生成一个超级块。这个块的重要性毋庸置疑,如果数据被损坏,则磁盘没办法使用。
三、打开文件
在linux里面,用open函数就可以打开一个文件。
函数原型:int open(const char*path,int flag)
函数如果打开成功,则返回一个文件描述符f ile d escribtor,这个描述符fd是一个非负整数。
1、文件描述符
在文件被打开后,需要用一个结构体来描述这个被打开的文件。这个结构体是struct file。
它包含了文件的读写指针、文件的长度、文件的属主等等重要属性。
另外,对于任意一个程序(进程),还有一个结构体,描述当前程序打开的所有文件。struct files_struct
fd就是fd_array数组的下标。打开一个文件后,生成一个struct file来保管文件的属性,然后将该结构体的首地址放到fd_array数组里面的一个空闲的位置,然后将该元素的下标返回。下次如果要访问这个文件,直接拿着下标去找就行了。
需要说明的是,对于任意一个程序来说,一般都会默认打开三个文件,键盘输入端、屏幕输出端、错误输出端。它们的编号分别为0,1,2。
在我们用open打开一个文件的时候,一般会在描述符里面找出一个最小的空闲描述符作为文件的描述符。利用这个技术,可以人为的关闭屏幕输出端1,然后再打开另外一个文件,这个时候,printf就会自动将文字输出那个文件里。
2、错误代码
在系统调用失败的时候,系统会自动往一个全局变量写一个数据,就是错误代码。这个全局变量是int errno。单单看这个变量是没意义的,必须要解读。
可以用strerror(errno)来将这个数据解读成一个字符串。
errno需要包含errno.h头文件,strerror需要包含string.h。
另外还可以使用perror来打印错误信息。
小结:
1、用户程序通过系统调用的方式让内核执行特定的任务,系统调用是通过软中