系统调用sys_write的过程

本文详细解析了Linux系统中用户空间如何通过系统调用进行写操作。以sys_write为例,介绍了从用户空间调用到内核空间的具体过程,包括sys_write函数的声明与实现、vfs_write的调用流程以及最终调用到具体的文件系统写函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

user space 只能通过系统调用来访问kernel提供的函数,下面以sys_read 为例
 sys_write函数声明在include/linux/syscalls.h文件中。这个文件中申明了linux kernel提供的所有系统调用
asmlinkage long sys_write(unsigned int fd, const char __user *buf, size_t count);
其函数实现在fs/read_write.c
SYSCALL_DEFINE3(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 = file_pos_read(f.file);
        //调用vfs 提供的写函数
        ret = vfs_write(f.file, buf, count, &pos);
        if (ret >= 0)
            file_pos_write(f.file, pos);
        fdput_pos(f);
    }

    return ret;
}
在这个系统调用中调用vfs提供的写函数vfs_write,vfs_write 通过在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(VERIFY_READ, buf, count)))
        return -EFAULT;

    ret = rw_verify_area(WRITE, file, pos, count);
    if (!ret) {
        if (count > MAX_RW_COUNT)
            count =  MAX_RW_COUNT;
        file_start_write(file);
        ret = __vfs_write(file, buf, count, pos);
        if (ret > 0) {
            fsnotify_modify(file);
            add_wchar(current, ret);
        }
        inc_syscw(current);
        file_end_write(file);
    }

    return ret;
}
vfs_write 中通过rw_verify_area 验证可以操作这个文件后,就调用__vfs_write开始写
ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
            loff_t *pos)
{
    if (file->f_op->write)
        return file->f_op->write(file, p, count, pos);
    else if (file->f_op->write_iter)
        return new_sync_write(file, p, count, pos);
    else
        return -EINVAL;
}
__vfs_write 这个函数中就调用具体文件系统的写函数,从可以可以看到优先调用file->f_op->write。
其中file->f_op是在调用文件系统注册的时候赋值的,这里以ext4 为例
const struct file_operations ext4_file_operations = {
    .llseek        = ext4_llseek,
    .read_iter    = generic_file_read_iter,
    .write_iter    = ext4_file_write_iter,
    .unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl    = ext4_compat_ioctl,
#endif
    .mmap        = ext4_file_mmap,
    .open        = ext4_file_open,
    .release    = ext4_release_file,
    .fsync        = ext4_sync_file,
    .get_unmapped_area = thp_get_unmapped_area,
    .splice_read    = generic_file_splice_read,
    .splice_write    = iter_file_splice_write,
    .fallocate    = ext4_fallocate,
};

这里的write是null,因此调用write_iter,也就是ext4_file_write_iter
ext4_file_write_iter->__generic_file_write_iter->generic_file_direct_write->filemap_write_and_wait_range->__filemap_fdatawrite_range->do_writepages->generic_writepages->write_cache_pages->__writepage->ext4_writepage->ext4_bio_write_page->io_submit_add_bh
可见最终的写操作还是在ext4 中实现,写操作由于不同的flag,走的flow有所不同,这里只是举例而已。
总结一下,user space 只能通过系统调用来调用kernel space 提供的接口函数。kernel space 对外通过vfs这个通用接口,各个文件系统自己实现read/write 接口,并把自己注册到文件系统的列表中.
<think>嗯,用户给了一段C代码,是关于BMS(电池管理系统)的一些函数,他们想让我解释一下void BMS_STA(void)这个函数,还有相关的其他函数。首先,我需要仔细阅读代码,理解每个函数的作用以及它们之间的调用关系。 首先看BMS_STA函数。这个函数里面调用了IIC1_read_one_byte读取SYS_CTRL2寄存器的值,然后赋值给BMS_sta。接着用位与操作取出DSG_STA和CHG_STA的状态,这两个可能对应放电和充电的状态位。然后把这些状态存到shang数组的特定位置,再放到can_buf7的缓冲区里,可能是为了CAN通信传输状态数据。 接下来是Only_Open_CHG函数,它先调用BMS_STA获取当前状态,然后根据DSG_STA是否为0来决定写入不同的值到SYS_CTRL2。这里用的是0x43或者0x41,可能是在设置充电和放电的控制位。如果放电状态不为0,就同时开启充电和放电?需要看寄存器的位定义。类似的,其他函数如Only_Close_CHG、Only_Open_DSG等,都是根据当前状态来设置不同的控制字节。 用户可能是在分析电池管理系统的代码,或者想了解这些函数如何控制充放电状态。他们可能希望理解每个函数的具体作用,以及寄存器的配置方式。可能需要解释这些控制字节的各个位代表什么,比如0x40、0x41等数值对应的二进制位,从而确定是开启还是关闭某个功能。 另外,代码中有一些可能的错误或需要注意的地方。例如,在BMS_STA函数中,DSG_STA和CHG_STA是通过位与操作得到的,但之后直接赋值给shang数组和can_buf7,可能需要确认这些变量是否需要进一步处理,比如移位或者其他转换。此外,函数如Only_Open_CHG中的条件判断是否合理,比如当DSG_STA不等于0时,写入0x43,否则0x41,这可能需要参考具体的数据手册来确定是否正确。 用户可能需要更详细的寄存器位定义解释,或者这些函数在整体系统中的作用。比如,为什么在打开充电时要检查放电状态,或者这些控制命令如何影响电池的充放电流程。此外,可能需要考虑多线程或中断环境下这些函数的安全性,比如是否有可能在读取状态后,状态发生变化,导致写入的值不正确。 总结来说,用户需要的是对每个函数的详细解释,以及它们如何协同工作来控制电池的充放电状态。同时,可能需要指出代码中的潜在问题或需要特别注意的地方,帮助用户更好地理解和维护这段代码。</think>以下是针对代码的逐层解析: 一、BMS_STA 函数分析 $$BMS\_sta = IIC1\_read\_one\_byte(SYS\_CTRL2);$$ 1. 核心功能:通过I²C总线读取`SYS_CTRL2`寄存器的8位状态值 2. 状态位解析: - `DSG_STA = BMS_sta & 0x02`:取第1位(二进制权值$2^1$)表示**放电状态** - `CHG_STA = BMS_sta & 0x01`:取第0位(二进制权值$2^0$)表示**充电状态** 3. 数据传递: - 将状态存入`shang[29]`和`shang[30]`(可能是状态数组) - 通过`can_buf7[3]`和`can_buf7[4]`将状态映射到CAN总线数据帧 二、充放电控制函数逻辑 1. 寄存器`SYS_CTRL2`的位定义(假设): ``` Bit1: DSG_EN (放电使能) Bit0: CHG_EN (充电使能) ``` 对应控制值: $$0x40 = 01\_000000b \quad (全关)$$ $$0x41 = 01\_000001b \quad (仅充电开)$$ $$0x42 = 01\_000010b \quad (仅放电开)$$ $$0x43 = 01\_000011b \quad (充放电全开)$$ 2. 典型函数示例: ```c void Only_Open_CHG(void) { BMS_STA(); if(DSG_STA != 0) // 当放电已激活时 { IIC1_write_one_byte_CRC(SYS_CTRL2, 0X43); // 保持放电,开启充电 } else { IIC1_write_one_byte_CRC(SYS_CTRL2, 0X41); // 仅开启充电 } } ``` 3. 控制策略: - 所有操作前先读取当前状态(确保状态同步) - 通过位掩码组合实现精准控制 - 写入时自动附加CRC校验(保证通信可靠性) 三、关键设计特点 1. 状态同步机制:每次控制操作前调用`BMS_STA()`确保获取最新状态 2. 安全互锁设计: - `Only_Open_CHG()`在开启充电时保留现有放电状态 - `Only_Close_DSG()`在关闭放电时保留充电状态 3. 直接控制接口: - `Open_DSG_CHG()`直接全开(0x43) - `Close_DSG_CHG()`直接全关(0x40) 四、潜在改进建议 1. 建议添加枚举类型增强可读性: ```c typedef enum { CTRL_ALL_OFF = 0x40, CTRL_CHG_ONLY = 0x41, CTRL_DSG_ONLY = 0x42, CTRL_ALL_ON = 0x43 } SysCtrl2_Values; ``` 2. 可增加状态校验机制: ```c if((value & 0xFC) != 0x40) { // 检查高位是否异常 // 错误处理 } ``` 3. 建议添加延时:在状态读取与写入操作之间增加适当延时(根据器件手册) 五、典型应用场景 1. 充电管理: - 系统调用`Only_Open_CHG()`启动充电 - 达到满充电压后调用`Only_Close_CHG()` 2. 过放保护: - 检测到低压时调用`Only_Close_DSG()` 3. 紧急关断: - 调用`Close_DSG_CHG()`立即切断所有通路 该代码段展现了一个典型的BMS状态机控制实现,通过精准的位操作实现安全可靠的电池管理功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值