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库就是一次性给你三个邮件的好邮递员啦~~ 底层IO操作函数的介绍: 不带缓冲的IO操作又叫做底层IO操作。主要有5个函数:open()、close()、read()、write()、lseek()。这些函数 特点就是不带缓存,直接就对文件进行操作。(好不害羞的样子,直接就对人家、、、嘿嘿~~想多了你) 下面是对这个五个函数的一些详细介绍: 错误:返回 -1 flag打开方式补充 O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。 以下三个常量同样是选用的,它们用于同步输入输出 O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。 错误代码:(均已E开头,将其去掉就是有关于错误的方面的单词或单词的缩写) EEXIST 参数pathname 所指的文件已存在,却使用了O_CREAT和O_EXCL旗标。 close()函数的语法: 所需头文件:#include<unistd.h> (头文件我们可以使用 man命令查看,如:man close) 函数原型:int close( int fd ); 函数传入值:fd(open返回的文件描述符) 函数返回值:成功:0,失败:-1
read()函数的语法: 所需头文件:#include <unistd.h> (使用man 2 read 命令查看) 函数原型:ssize_t read(int fd,void *buf,size_t cout); 函数传入值:fd,文件描述符(想读哪个文件就传哪个文件描述符) buf,存储ner的内存空间(读到哪去,就是读到buf里面来) count,读取的字节数(想读多少) 函数返回值:成功:0,失败:-1
write()函数的语法: 所需头文件:#include <unistd.h> (使用man 2 write 命令查看) 函数原型:ssize_t write (int fd,void *buf,size_t cout); 函数传入值:fd,文件描述符(想对个文件进行写操作就传哪个文件描述符) buf,存储ner的内存空间(把buf里面的内容写到fd相关联的文件里面去) count,想写的字节数(想写多少) 函数返回值:成功:0,失败:-1
lseek()函数语法: 所需要头文件:#include <unistd.h> #include <sys/types.h> 函数原型:ssize_t lseek(int fd,off_t offset,int whence); 函数传入值:fd,文件描述符;offset,偏移量; whence(基点)可以取以下三种 SEEK_SET:文件开头+offset为新读写位置 SEEK_CUR: 目前读写位置+offset为新读写位置 SEEK_END:文件结尾+offset为新读写位置 函数返回值:成功:0,失败:-1
这节就介绍到这里吧,下节继续:嘿嘿~~ |
|
来自: huihaoking > 《linux函数》