一、文件描述符
概念:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
文件描述符主要用于read、write、close、lseek
二、文件操作函数
1、int open(const char *pathname,int flag,/*int mode*/);
pathname:指定打开的文件的路径+文件名
flag:操作模式:以O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写),O_CREAT(创建)文件不存在就创建
mode:指定新创建文件的权限,只有在O_CREAT时,才会需要
功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
返回值:-1,出错
2、int read (int fd,void*buff,int size);
fd:将要读取数据的文件描述符
buff:指定获取的数据在程序中存储的起始位置
size:本次最多读取的字节个数,一般为缓冲区的大小
返回值:-1,出错 成功:返回读到的字节个数
功能描述: 从文件读取数据。
3、int write ( int fd,void *buff ,int datalen);
fd:写数据的文件描述符
buff:指定需要写入数据的起始位置
datalen:写入数据的长度
返回值:-1,出错 成功:返回写入的字节个数
功能描述:向文件写入数据
4、int close(int fd);
fd:文件描述符
返回值:0成功,-1出错
功能描述:用于关闭一个文件
5、int lseek(int fd,int pos,int flag);
pos:位置
flag:标记
SEEK_SET 开始,当前位置为文件的开头,新位置为偏移量的大小
SEEK_CUR 当前,当前位置为指针的位置,新位置为当前位置加上偏移量
SEEK_END 尾/结束,当前位置为文件的结尾,新位置为文件大小加上偏移量的大小
返回值:失败返回-1,成功返回当前位移
三、系统调用与库函数的区别
进程直接调用内核暴露出来的的接口的方式称为系统调用;而调用将内核暴露出来的借口封装好的函数的方法为库函数的调用,有的库函数不需要封装内核暴露出来的接口。
库函数:在用户态调用,在用户态执行
系统调用函数:在用户态调用,在内核态执行
问题:
库函数与系统调用函数哪个执行效率高?
当处理的数据量比较小时,函数库的函数执行效率比较好,因为函数库的函数的作法是将要处理的数据先存入缓冲区内,等到缓冲区装满了,再将数据一次写入或者读出。这种方式处理小量数据时效率比较高。但是在进行系统调用时,因为用户进程从用户模式进入系统内核模式,中间涉及了许多额外的任务的切换工作,这些操作称为上下文切换,此类的额外工作会影响系统的执行效率。那么为什么还要使用系统调用呢?这是因为,读写文件通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),使用库函数调用可以大大减少系统调用的次数。这是因为缓冲区技术。在用户空间和内核空间,对文件操作都使用了缓冲区,当内核缓冲区写满之后或写结束之后才将内核缓冲区内容写到文件对应的硬件媒介中。
系统调用函数与库函数的联系
系统调用通常是用于底层文件的访问,例如在驱动程序中对设备文件的直接访问,而库函数是对系统调用的一层封装,因此在用库函数对文件操作的时候,必然会引起系统调用。也就是说,库函数调用实际上是通过系统调用实现的,例如:C库函数fwrite()就是通过write实现的。
四、用户态切换内核态
1、调用系统调用函数 → 软中断
2、缺页异常 → 硬中断
五、缓冲区
分为输入缓冲区(scanf)、输出缓冲区(printf)
输出缓冲区的条件:
1、程序结束(不能以_exit/_Exit结束);
2、遇到“\n”;
3、主动刷新fflush(stdout);
4、缓冲区满