【初探Linux】窥探Read系统调用源码
系统调用—Read
read简介
系统调用read的作用是:从与文件描述符fd相关联的文件中读取 n bytes个字节的数据,并把它放入到数据缓冲区buf中。
概要:
read函数在<unistd.h>
头文件中定义。
原型是:ssize_t read(int fd, void *buf, size_t count);
说明:
read()函数尝试从文件描述符fd中读取count个字节到buf开头的缓冲区中。
如果count=0;read返回0,并且没有其他结果,如果count>SSIZE_MAX,结果未指定。
ssize_t:有符号整型,与long类似。typedef long size_t
size_t:无符号的ssize_t,:typedef unsigned long size_t
返回值
调用成功返回读取的字节数,文件指示符指到对应的位置。这个返回值可能会比count,比如以下情况:
当文件的整体字节比count小时,读到文件尾。
我们从管道或者终端读取
我们读取时被一个信号打断了等等情况。
调用失败的时候返回-1;并且errno会被设置,这时候文件的指示符位置变化与否是未知的。
**open
**:打开或创建文件,返回文件描述符(File Descriptor)。
**read
**:从文件描述符读取数据到缓冲区。
**write
**:将缓冲区数据写入文件描述符对应的文件。
read源码
搜索sys_read
:https://elixir.bootlin.com/linux/v6.8.1/source/fs/read_write.c#L627
// fs/read_write.c |
看看struct fd
,这里的fd
看起来是文件的实体
struct fd { |
那么ksys_read
的传入参数中的无符号整数的fd
又是什么呢,fd
是进程中文件数组的索引,用于找到对应的file
回来继续看file_ppos
,它返回的pos
还不是很懂,ppos
是position pointer
的缩写,文件的位置指针,file_ppos
函数首先看文件的类型(普通文件or流式文件),如果是普通文件就返回文件位置pos
,否则返回NULL
/* file_ppos returns &file->f_pos or NULL if file is stream */ |
嗯,pos变量只是被声明了,并没有初始化
loff_t pos, *ppos = file_ppos(f.file); |
顺便去看看loff_t
,貌似是一个long long
类型,表示 文件偏移量(file offset),用于处理大文件的读写和定位操作
typedef __kernel_loff_t loff_t; |
那接下来就是看看读取文件内容并存放到缓存区的核心函数vfs_read
了
// fs/read_write.c |
read
是传统的同步读取接口,而read_iter
是为了支持异步I/O和更复杂的迭代操作设计的。新的代码可能更倾向于使用read_iter
,但为了兼容旧代码,内核保留了read
方法。因此,在vfs_read
中,优先检查是否有read
实现,如果没有,再尝试read_iter
。继续往下追,看看主要的read
函数,这里的read
就是调用了具体文件系统的read
函数,以ext4
为例
// /fs/ext4/file.c |
总结一下,read
系统调用干了什么事情,找到文件file
的位置,调用虚拟文件系统函数vfs_read
读取文件内容,vfs_read
经过一系列验证文件和缓冲区的合法性,然后调用具体的文件系统实体的read
函数,将磁盘文件中的数据读取到内核缓冲区,最后复制到用户空间缓冲区。
参考资料: