网站建设一般分为几个步骤,WordPress仿w3c,婚纱网站建设 最开始,孝感有做网站的公司吗诉求#xff1a;遇到一个问题 echo blocked /sys/class/block/sdb/device/state 报非法参数#xff0c;想要知道根因#xff0c;但是对这块内核代码不熟悉#xff0c;不知道从哪里下手#xff0c;那就先用ftrace看看内核调用栈#xff0c;如下所示。
rootrlk:/home…诉求遇到一个问题 echo blocked /sys/class/block/sdb/device/state 报非法参数想要知道根因但是对这块内核代码不熟悉不知道从哪里下手那就先用ftrace看看内核调用栈如下所示。
rootrlk:/home/rlk/rlk# echo blocked /sys/class/block/sdb/device/state
bash: echo: write error: Invalid argumentcd /sys/kernel/debug/tracing 设定跟踪的进程pid set_ftrace_pid 查看可以设定的tracer
rootrlk:/home/rlk/rlk# cat /sys/kernel/debug/tracing/available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop设定tracer类型
rootrlk:/sys/kernel/debug/tracing# echo function_graph current_tracer开启tracer
rootrlk:/sys/kernel/debug/tracing# echo 1 tracing_on写文件/sys/class/block/sdb/device/state是一个很快的过程没有办法精准的知道什么时候把trace打开所以只能采集相对来说比较泛的数据为了减少一些无用的数据可以通过写两个脚本来实现先抓取echo running /sys/class/block/sdb/device/state的调用栈。
write.sh
#!/bin/bash
sleep 0.3
echo running /sys/class/block/sdb/device/state查找进程pid命令花费的时间为0.109s
rootrlk:/home/rlk/rlk# time ps aux | grep write.sh | grep -v grep | awk {print $2}
5442real 0m0.109s
user 0m0.019s
sys 0m0.121s#!/bin/bash
./write.sh
pidps aux | grep write.sh | grep -v grep | awk {print $2}
echo $pidecho $pid /sys/kernel/debug/tracing/set_ftrace_pid
echo 1 /sys/kernel/debug/tracing/tracing_onecho 其实就是一个打开文件然后写文件的过程用户态写文件会调用vfs_write在trace日志中抓出如下调用信息。 1) | do_syscall_64() {1) | __x64_sys_write() {1) | ksys_write() {1) | __fdget_pos() {1) 0.100 us | __fget_light();1) 0.291 us | }1) | vfs_write() {1) | rw_verify_area() {1) | security_file_permission() {1) | apparmor_file_permission() {1) | common_file_perm() {1) 0.100 us | aa_file_perm();1) 0.290 us | }1) 0.481 us | }1) 0.682 us | }1) 0.872 us | }1) | __sb_start_write() {1) | _cond_resched() {1) 0.090 us | rcu_all_qs();1) 0.271 us | }1) 0.461 us | }1) | __vfs_write() {1) | kernfs_fop_write() {1) | __kmalloc() {1) 0.090 us | kmalloc_slab();1) | _cond_resched() {1) 0.091 us | rcu_all_qs();1) 0.291 us | }1) 0.090 us | should_failslab();1) 0.330 us | memcg_kmem_put_cache();1) 1.513 us | }1) | __check_object_size() {1) 0.100 us | check_stack_object();1) 0.090 us | __virt_addr_valid();1) 0.491 us | __check_heap_object();1) 1.202 us | }1) | mutex_lock() {1) | _cond_resched() {1) 0.090 us | rcu_all_qs();1) 0.261 us | }1) 0.451 us | }1) 0.090 us | kernfs_get_active();1) | sysfs_kf_write() { //调用sfsfs写接口1) | dev_attr_store() {1) | store_state_field() {1) | mutex_lock() {1) | _cond_resched() {1) 0.100 us | rcu_all_qs();1) 0.271 us | }1) 0.461 us | }1) 0.200 us | scsi_device_set_state(); //在这里设置了device的状态1) | blk_mq_run_hw_queues() {1) | blk_mq_run_hw_queue() {1) 0.390 us | dd_has_work();1) 2.084 us | }1) 2.775 us | }1) 0.090 us | mutex_unlock();1) 4.628 us | }1) 5.210 us | }1) 6.191 us | }1) 0.101 us | kernfs_put_active();1) 0.090 us | mutex_unlock();1) 0.091 us | kfree();1) 10.841 us | }1) 11.071 us | }1) 0.091 us | kfree();1) 10.841 us | }1) 11.071 us | }1) 0.090 us | __fsnotify_parent();1) 0.090 us | fsnotify();1) 0.080 us | __sb_end_write();1) 13.415 us | }1) 14.006 us | }1) 14.187 us | }1) 0.090 us | fpregs_assert_state_consistent();1) 14.627 us | }
drivers/scsi/scsi_sysfs.c定义了 /sys/class/block/sdx/device/state操作函数所有块设备相关ll /sys/class/block/sdx/device/DEVICE_ATTR都可以在这个文件中找到测试的内核版本为5.4.0-26-generic //
static const struct {enum scsi_host_state value;char *name;
} shost_states[] {{ SHOST_CREATED, created },{ SHOST_RUNNING, running },{ SHOST_CANCEL, cancel },{ SHOST_DEL, deleted },{ SHOST_RECOVERY, recovery },{ SHOST_CANCEL_RECOVERY, cancel/recovery },{ SHOST_DEL_RECOVERY, deleted/recovery, },
};
const char *scsi_host_state_name(enum scsi_host_state state)
{int i;char *name NULL;for (i 0; i ARRAY_SIZE(shost_states); i) {if (shost_states[i].value state) {name shost_states[i].name;break;}}return name;
}static ssize_t
store_state_field(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{int i, ret;struct scsi_device *sdev to_scsi_device(dev);enum scsi_device_state state 0;for (i 0; i ARRAY_SIZE(sdev_states); i) {const int len strlen(sdev_states[i].name);if (strncmp(sdev_states[i].name, buf, len) 0 buf[len] \n) {state sdev_states[i].value;break;}}switch (state) {case SDEV_RUNNING:case SDEV_OFFLINE:break;default:return -EINVAL;}mutex_lock(sdev-state_mutex);ret scsi_device_set_state(sdev, state);/** If the device state changes to SDEV_RUNNING, we need to run* the queue to avoid I/O hang.*/if (ret 0 state SDEV_RUNNING)blk_mq_run_hw_queues(sdev-request_queue, true);mutex_unlock(sdev-state_mutex);return ret 0 ? count : -EINVAL;
}static ssize_t
show_state_field(struct device *dev, struct device_attribute *attr, char *buf)
{struct scsi_device *sdev to_scsi_device(dev);const char *name scsi_device_state_name(sdev-sdev_state);if (!name)return -EINVAL;return snprintf(buf, 20, %s\n, name);
}static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);以上问题可以在上面的代码找到答案当前仅支持设置SDEV_RUNNING,SDEV_OFFLINE两种状态。
DEVICE_ATTR 是一个宏定义用于在 Linux 设备驱动程序中定义设备属性。它定义了一个名为 dev_attr_ 的静态结构体变量其中 是属性的名称。该结构体包含了属性的名称、读取和写入函数的指针以及一些其他属性。
使用 DEVICE_ATTR 宏可以方便地定义设备属性而无需手动编写结构体和函数。例如以下代码定义了一个名为 my_attribute 的设备属性
static ssize_t my_attribute_show(struct device *dev, struct device_attribute *attr, char *buf)
{// 读取属性值并将其写入缓冲区return sprintf(buf, Hello, world!\n);
}static ssize_t my_attribute_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{// 将缓冲区中的值写入属性return count;
}DEVICE_ATTR(my_attribute, 0644, my_attribute_show, my_attribute_store);在上面的代码中my_attribute_show 和 my_attribute_store 分别是读取和写入函数的指针。0644 是属性的访问权限表示该属性可以被所有者读取和写入其他用户只能读取。最后一行使用 DEVICE_ATTR 宏定义了 my_attribute 属性。
Linux 内核的 IO 调用栈通常包括以下几个层次
用户空间调用应用程序通过系统调用如 read、write、open 等向内核发起 IO 请求。
VFS 层VFSVirtual File System是 Linux 内核中的一个抽象层它负责管理文件系统的挂载、卸载、文件名解析等操作。当应用程序发起 IO 请求时VFS 层会根据文件系统类型和文件描述符等信息将请求转发给相应的文件系统。
文件系统层文件系统层负责具体的 IO 操作包括读写磁盘、缓存管理、文件系统元数据更新等。不同的文件系统有不同的实现方式但它们都需要遵循 VFS 层的接口规范。
块设备层块设备层负责将 IO 请求转换为磁盘操作。它通过与硬件驱动程序的交互将数据从内核缓冲区写入磁盘或从磁盘读取数据到内核缓冲区。
硬件驱动程序硬件驱动程序负责与硬件设备进行通信将 IO 请求转换为硬件操作。它通过与设备控制器的交互将数据从内存写入磁盘或从磁盘读取数据到内存。
总的来说Linux 内核的 IO 调用栈是一个由多个层次组成的复杂系统每个层次都有自己的职责和实现方式。在 IO 请求的处理过程中数据需要在不同的层次之间传递和转换因此 IO 性能的优化需要考虑整个调用栈的影响。