Linux VFS文件系统分析7(基于Linux6.6)---VFS与write接口介绍
一、write接口
write调用和read调用实现的功能类似,唯一不同的为read调用的是vfs_read实现文件读操作,而write调用的是vfs_write实现文件写操作。
write接口实现的内容如下:
1.该接口首先从当前进程的files指针中获取fd对应的文件描述符;
2.若获取文件描述符成功,则调用file_pos_read获取当前文件的offset;
3.调用vfs_write接口,从而调用该文件对应inode节点提供的write接口,实现write操作;
4.调用file_pos_write更新当前文件的offset;
5.调用fdput解除对文件描述符的引用计数。
该接口的实现流程图如下所示,该流程图说明了write接口的实现逻辑。

该接口的代码如下:
fs/read_write.c
ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos, *ppos = file_ppos(f.file);
if (ppos) {
pos = *ppos;
ppos = &pos;
}
ret = vfs_write(f.file, buf, count, ppos);
if (ret >= 0 && ppos)
f.file->f_pos = pos;
fdput_pos(f);
}
return ret;
}
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}
该接口中调用了vfs_write,关于vfs_write定义,我们再继续分析。
二、vfs_write接口分析
针对vfs_write接口,其实现的功能有:
1.若文件描述符不存在写取权限,返回失败;
2.若应用层传递的内存指针没有读权限,返回失败;
3.若该文件描述符没有write或aio_write接口,则返回失败;
4.调用rw_verify_area检测是否可对文件进行写取操作(如是否别的进程对文件加锁、要读取的大小是否合法等);
5.若file->f_op->write接口存在,则调用文件inode的write接口,进行文件读取操作;若不存在,则调用do_sync_write,调用aio_write;
6.调用fsnotify_access,发送文件被读取的通知;
7.增加写字符计数。
fs/read_write.c
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL;
if (unlikely(!access_ok(buf, count)))
return -EFAULT;
ret = rw_verify_area(WRITE, file, pos, count);
if (ret)
return ret;
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
file_start_write(file);
if (file->f_op->write)
ret = file->f_op->write(file, buf, count, pos);
else if (file->f_op->write_iter)
ret = new_sync_write(file, buf, count, pos);
else
ret = -EINVAL;
if (ret > 0) {
fsnotify_modify(file);
add_wchar(current, ret);
}
inc_syscw(current);
file_end_write(file);
return ret;
}
三、应用举例
3.1、VFS 与 write 接口的工作原理
当应用程序调用 write 系统调用时,VFS 层会根据文件描述符的类型,决定如何将数据写入具体的文件或设备。
- 文件描述符:
write操作需要一个已经打开的文件描述符,通常是通过open系统调用获取的。 - VFS 层:
VFS层会将write调用转发给具体的文件系统或设备驱动程序。对于不同类型的文件,VFS 会调用相应的操作方法。 - 文件系统的具体实现:例如,对于普通文件(如 ext4、NTFS),会有
write系统调用的实现;对于设备文件(如/dev/null或/dev/random),则由相应的设备驱动程序处理。
3.2、write 示例应用
以下是几种常见情景下的 write 调用示例,包括写入普通文件、设备文件和网络文件。
1.示例 1:向本地普通文件写入数据
这是一个常见的例子,我们通过 write 向本地普通文件写入数据。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main() {
const char *file_path = "/tmp/testfile.txt"; // 假设这是一个文件路径
int fd;
const char *data = "Hello, Linux!\n"; // 要写入的内容
ssize_t bytes_written;
// 打开文件(如果文件不存在,则创建)
fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 写入数据到文件
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("write");
close(fd);
return 1;
}
printf("Written %zd bytes to %s\n", bytes_written, file_path);
// 关闭文件描述符
close(fd);
return 0;
}
工作流程:
open系统调用:VFS层识别/tmp/testfile.txt为一个本地文件,并根据文件权限(O_WRONLY | O_CREAT | O_TRUNC)将文件打开。此时,VFS会通过具体的文件系统(如ext4)调用文件系统的open函数。write系统调用:write将数据写入文件时,VFS会调用具体文件系统(如ext4)的write函数,最终通过磁盘 I/O 写入数据。- 关闭文件描述符:
close系统调用将释放文件描述符,VFS会调用相应的文件系统实现来释放资源。
2.示例 2:写入设备文件(如 /dev/null)
设备文件是一种特殊类型的文件,写入数据时会根据设备的特性做不同的处理。例如,向 /dev/null 写入任何数据时,数据都会被丢弃。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
const char *dev_path = "/dev/null"; // 特殊设备文件
int fd;
const char *data = "This will be discarded\n"; // 要写入的数据
ssize_t bytes_written;
// 打开设备文件
fd = open(dev_path, O_WRONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 向设备写入数据
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("write");
close(fd);
return 1;
}
printf("Written %zd bytes to %s (data discarded)\n", bytes_written, dev_path);
// 关闭文件描述符
close(fd);
return 0;
}
工作流程:
open系统调用:VFS层识别/dev/null为设备文件,并将操作传递给null设备驱动。write系统调用:写入的数据会被丢弃,VFS会通过设备驱动的write函数来处理。对于/dev/null设备,数据会直接丢弃,不会做任何存储。- 关闭文件描述符:操作结束后,调用
close来释放文件描述符。
3.示例 3:写入网络文件(如通过 NFS 挂载的文件)
如果你将远程文件系统(如 NFS)挂载到本地,你可以像访问本地文件一样向网络文件写入数据。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
const char *nfs_path = "/mnt/nfs/share/testfile.txt"; // NFS 挂载的路径
int fd;
const char *data = "Hello from NFS!\n"; // 要写入的数据
ssize_t bytes_written;
// 打开远程 NFS 文件
fd = open(nfs_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 向 NFS 文件写入数据
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("write");
close(fd);
return 1;
}
printf("Written %zd bytes to %s (via NFS)\n", bytes_written, nfs_path);
// 关闭文件描述符
close(fd);
return 0;
}
工作流程:
open系统调用:VFS层识别/mnt/nfs/share/testfile.txt为一个网络文件,并将操作转发给 NFS 客户端。write系统调用:通过VFS层,写入操作会发送到远程 NFS 服务器,数据通过网络传输并存储到远程服务器上的文件系统。- 关闭文件描述符:与本地文件一样,操作结束后通过
close来释放文件描述符。
4.示例 4:向 /dev/random 写入数据
/dev/random 是一个只读设备文件,它用于提供高质量的随机数。尝试向其写入数据会导致错误。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
const char *dev_path = "/dev/random"; // 随机数设备
int fd;
const char *data = "This won't be written\n"; // 想要写入的数据
ssize_t bytes_written;
// 打开设备文件
fd = open(dev_path, O_WRONLY); // 以写方式打开
if (fd == -1) {
perror("open");
return 1;
}
// 尝试写入数据(会失败)
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("write"); // 会报告错误,因为 /dev/random 是只读设备
close(fd);
return 1;
}
printf("Written %zd bytes to %s\n", bytes_written, dev_path);
// 关闭文件描述符
close(fd);
return 0;
}
工作流程:
open系统调用:VFS层识别/dev/random为只读设备,open会失败,因为设备不允许写入。write系统调用:如果应用程序尝试写入数据,write会失败并返回-1,并通过perror显示错误信息。
4091

被折叠的 条评论
为什么被折叠?



