rt-linux下的cgroup cpu的死锁bug

一、背景

rt-linux系统有其非常大的实时性的优势,但是与之俱来的是该系统上有一些天然的缺陷。由于rt-linux系统允许进程在内核态执行的逻辑里,在持锁期间,甚至持spinlock锁期间,都能被其他进程抢占。这一特性能带来实时性的好处,即能最大限度的满足高优先级进程的优先运行,但是势必会带来一定坏处,也就是某些底层锁会因此在持有的情况下被迫不能继续执行(被别的任务抢占),导致别的任务也需要访问该底层锁的时候,就会阻塞,这种情况会因为开启cgroup后更加加剧。

它会导致一些看似很不合理的情况,比如两个看似毫无关系的进程却有因为其中一个执行到一半被cgroup限额了导致另外一个进程一直在等前者继续运行从而来唤醒自己。老实说,这种情况应该也能在普通内核开启cgroup的cpu限额后也能出现,但是普通内核由于spinlock是禁用抢占的,而底层逻辑里的锁大部分还是spinlock,所以普通内核里因为cgroup限额导致底层逻辑阻塞两个看似不相关的进程的情况还是比较少的。另外一方面,普通内核里如果发生类似的情况就是使用了mutex,而使用了mutex那就是可以认为是能忍受一定的等待的,同样的,对于底层逻辑也一样,底层逻辑里要使用mutex的地方肯定也都是默认能忍受一定等待的,那么这时候发生cgroup cpu的限额导致这样的等待又多持续了一段时间,那也理应能接受的。

虽然rt-linux有针对锁有优先级继承的逻辑来保证杜绝优先级反转的情况,比如如果普通进程访问了一个spinlock但是被RT进程抢占了,而RT进程执行完之后,普通进程所在的cgroup组又限额导致普通进程不能进一步执行逻辑,而导致spinlock一直退不出来。这时候,如果是一个实时进程要访问该锁,那么由于优先级继承的逻辑,普通进程就会被临时提高优先级,提到到要用锁的进程的优先级和自己优先级中的较高者,这个例子里就是提高到实时优先级,所以普通进程又能执行下去了。关于该优先级反转的进一步细节见之前的博客 rt-linux之防止优先级反转-CSDN博客 。

但是,刚才说是要使用该锁的进程是实时进程的情况,但是如果要使用该锁的后者并不是实时进程,而是普通进程的话,那么就算提高优先级也是在普通进程这个调度类范畴,也受cgroup cpu的管控,仍然无法拿到额外的运行时间,还得等cgroup cpu的period timer重新补充时间来运行。这就会导致后面要拿锁的进程要等很长的时间,而后面要拿锁的进程和前者持锁被throttle的进程可能它们之间是表面并不关联的,关联的部分可能就是底层的逻辑,这就会导致一些比较诡异难理解的现象出来。这种现象,我们后面的博客会用一些例子程序来模拟出来,并用图示来进一步解释。

这篇博客里,我们只讨论rt-linux内核里会因为cgroup限额导致死锁的情况,在下面第二章里,我们给出复现的程序和复现方法,在第三章里,我们来阐述其原理,并给出相应的解法。

二、复现程序和复现方法

2.1 复现程序

2.1.1 复现程序用的内核模块

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <trace/events/workqueue.h>
#include <linux/sched/clock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/tracepoint.h>
#include <trace/events/osmonitor.h>
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/kmem.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/sched/task_stack.h>
#include <linux/nmi.h>
#include <linux/version.h>
#include <linux/sched/mm.h>
#include <asm/irq_regs.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/stop_machine.h>struct kprobe _kp1;//static bool _blog = false;int getfullpath(struct inode* inode, char* i_buffer, int i_len)
{struct dentry* dentry;//printk("inode = %ld\n", inode->i_ino);//spin_lock(&inode->i_lock);hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {char* buffer, * path;buffer = (char*)__get_free_page(GFP_KERNEL);if (!buffer)return -ENOMEM;path = dentry_path_raw(dentry, buffer, PAGE_SIZE);if (IS_ERR(path)) {continue;}strlcpy(i_buffer, path, i_len);//printk("dentry name = %s , path = %s", dentry->d_name.name, path);free_page((unsigned long)buffer);}//spin_unlock(&inode->i_lock);return 0;
}#define DEVICE_NAME "testcgroupbug"
#define IOCTL_SET_MODE _IOW('a', 1, long)static struct proc_dir_entry* proc_entry;
// static rwlock_t my_rwlock;
DEFINE_RWLOCK(my_rwlock);typedef struct testpara {int mode;int sleepsecond;
} testpara;// static ssize_t proc_read(struct file *file, char __user *buf, size_t count, loff_t *offset) {
//     char message[256];
//     ssize_t len = snprintf(message, sizeof(message), "Current mode: %d\n", mode);//     return simple_read_from_buffer(buf, count, offset, message, len);
// }void deadloop_second(int i_time) {unsigned long start_time = jiffies;if (i_time < 0) {while (1);return;}else if (i_time == 0) {return;}else {while (time_before(jiffies, start_time + (unsigned long)(HZ * i_time))) {}}
}#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>struct perf_event* __percpu* sample_hbp;static void sample_hbp_handler(struct perf_event* bp,struct perf_sample_data* data,struct pt_regs* regs)
{//printk(KERN_INFO "%s value is changed\n", ksym_name);dump_stack();//printk(KERN_INFO "Dump stack from sample_hbp_handler\n");
}void register_prioritychange_dumpstack(void)
{int ret;struct perf_event_attr attr;void* addr = &current->prio;hw_breakpoint_init(&attr);attr.bp_addr = (unsigned long)addr;attr.bp_len = HW_BREAKPOINT_LEN_4;attr.bp_type = HW_BREAKPOINT_W;sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);if (IS_ERR((void __force*)sample_hbp)) {ret = PTR_ERR((void __force*)sample_hbp);return;}printk(KERN_INFO "HW Breakpoint for write installed\n");}void unregister_prioritychange_dumpstack(void)
{unregister_wide_hw_breakpoint(sample_hbp);
}static bool _bneedoutput = false;static enum hrtimer_restart hrtimer_callback(struct hrtimer* timer)
{printk(KERN_INFO "<cpu%d><comm%s>hrtimer_callback before read_lock!\n", smp_processor_id(), current->comm);read_lock(&my_rwlock);printk(KERN_INFO "<cpu%d><comm%s>hrtimer_callback before read_unlock!\n", smp_processor_id(), current->comm);read_unlock(&my_rwlock);printk(KERN_INFO "<cpu%d><comm%s>hrtimer_callback after read_unlock!\n", smp_processor_id(), current->comm);return HRTIMER_NORESTART;
}static struct hrtimer		_testtimer;void register_hrtimer_soft(void)
{hrtimer_init(&_testtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);_testtimer.function = hrtimer_callback;hrtimer_forward_now(&_testtimer, ns_to_ktime(10000));hrtimer_start_expires(&_testtimer, HRTIMER_MODE_ABS_PINNED);
}void unregister_hrtimer_soft(void)
{hrtimer_cancel(&_testtimer);
}static volatile bool bhasreadenterlock = false;static volatile bool bhaswriter = false;static volatile bool bhasregisterhrtimersoft = false;void deadloop_second_special(int i_time) {unsigned long start_time = jiffies;if (i_time < 0) {while (1);return;}else if (i_time == 0) {return;}else {while (time_before(jiffies, start_time + (unsigned long)(HZ * i_time))) {if (bhaswriter) {{unsigned long start = jiffies;while (time_before(jiffies, start + (unsigned long)(HZ / 25))) {}}if (!bhasregisterhrtimersoft) {bhasregisterhrtimersoft = true;register_hrtimer_soft();}}}}
}static int _readindex = 0;
static int _writeindex = 0;static long proc_ioctl(struct file* file, unsigned int cmd, unsigned long arg) {testpara para;if (cmd == IOCTL_SET_MODE) {if (copy_from_user(&para, (int __user*)arg, sizeof(testpara))) {return -EFAULT;}if (para.mode == 0) {int readindex = _readindex;_bneedoutput = true;_readindex++;if (bhaswriter) {printk(KERN_ERR "<cpu%d>readindex[%d]Not expected bhaswriter value! Need reload ko!\n", smp_processor_id(),readindex);return -EFAULT;}//register_prioritychange_dumpstack();printk(KERN_INFO "<cpu%d>readindex[%d]before read lock\n", smp_processor_id(), readindex);read_lock(&my_rwlock);bhasreadenterlock = true;printk(KERN_INFO "<cpu%d>readindex[%d]Read lock acquired.\n", smp_processor_id(), readindex);printk(KERN_INFO "<cpu%d>readindex[%d]Sleep second[%d]\n", smp_processor_id(), readindex, para.sleepsecond);// wait bhaswriter true and then register_hrtimer_softdeadloop_second_special(para.sleepsecond);// Perform read operations hereread_unlock(&my_rwlock);printk(KERN_INFO "<cpu%d>readindex[%d]Read lock released.\n", smp_processor_id(), readindex);//unregister_prioritychange_dumpstack();}else if (para.mode == 1) {int writeindex = _writeindex;_writeindex++;printk(KERN_INFO "<cpu%d>writerindex[%d]writer check bhasreadenterlock\n", smp_processor_id(), writeindex);while (1) {if (bhasreadenterlock) break;}printk(KERN_INFO "<cpu%d>writerindex[%d]before write lock\n", smp_processor_id(), writeindex);bhaswriter = true;write_lock(&my_rwlock);printk(KERN_INFO "<cpu%d>writerindex[%d]Write lock acquired.\n", smp_processor_id(), writeindex);printk(KERN_INFO "<cpu%d>writerindex[%d]Sleep second[%d]\n", smp_processor_id(), writeindex, para.sleepsecond);deadloop_second(para.sleepsecond);// Perform write operations herewrite_unlock(&my_rwlock);printk(KERN_INFO "<cpu%d>writerindex[%d]Write lock released.\n", smp_processor_id(), writeindex);_bneedoutput = false;}else {printk(KERN_ERR "<cpu%d>Invalid mode: %d\n", smp_processor_id(), para.mode);return -EINVAL;}return 0;}return -EINVAL;
}static const struct proc_ops proc_fops = {.proc_ioctl = proc_ioctl,
};static int proctestcgroup_init(void) {proc_entry = proc_create(DEVICE_NAME, 0666, NULL, &proc_fops);if (!proc_entry) {return -ENOMEM;}rwlock_init(&my_rwlock);printk(KERN_INFO "Module loaded: /proc/%s created.\n", DEVICE_NAME);return 0;
}static void proctestcgroup_exit(void) {proc_remove(proc_entry);printk(KERN_INFO "Module unloaded: /proc/%s removed.\n", DEVICE_NAME);
}int kprobecb_vdso_fault_pre(struct kprobe* i_k, struct pt_regs* i_p)
{if (_bneedoutput) {printk(KERN_INFO "<cpu%d><comm%s>sched_cfs_period_timer\n", smp_processor_id(), current->comm);//dump_stack();// {//     struct kiocb *iocb = (struct kiocb *)i_p->di;//     struct file *file = iocb->ki_filp;//     struct address_space *mapping = file->f_mapping;//     struct inode *inode = mapping->host;//     int ret = 0;//     char buf[128];//     if ((ret = getfullpath(inode, buf, 128)) < 0) {//         return 0;//     }//     printk("generic_file_write_iter file[%s]\n",//         buf);// }}return 0;
}int kprobe_register_func_vdso_fault(void)
{int ret;memset(&_kp1, 0, sizeof(_kp1));_kp1.symbol_name = "sched_cfs_period_timer";_kp1.pre_handler = kprobecb_vdso_fault_pre;_kp1.post_handler = NULL;ret = register_kprobe(&_kp1);if (ret < 0) {printk("register_kprobe fail!\n");return -1;}printk("register_kprobe success!\n");return 0;
}void kprobe_unregister_func_vdso_fault(void)
{unregister_kprobe(&_kp1);
}static int __init testcgroupbug_init(void)
{kprobe_register_func_vdso_fault();proctestcgroup_init();return 0;
}static void __exit testcgroupbug_exit(void)
{kprobe_unregister_func_vdso_fault();proctestcgroup_exit();
}module_init(testcgroupbug_init);
module_exit(testcgroupbug_exit);
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("Module for testcgroupbug debug.");
MODULE_LICENSE("GPL");

2.1.2 复现程序用的rwlock_read用户态程序

#include <cstring>
#include <iostream>
#include <csignal>
#include <thread>
#include <chrono>
#include <ctime>
#include <atomic>
#include <cmath>
#include <fstream>
#include <vector>
#include <memory>
#include <map>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/ioctl.h>
#include <linux/types.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/syscall.h>#define DEVICE_NAME "testcgroupbug"
#define IOCTL_SET_MODE _IOW('a', 1, long)typedef struct testpara {int mode;int sleepsecond;
} testpara;#define IOCTL_SET_MODE _IOW('a', 1, long)int main(int argc, char *argv[])
{if (argc != 2) {fprintf(stderr, "Usage: %s <sleep_time_in_seconds>\n", argv[0]);return 1;}int duration = atoi(argv[1]);int fd = open("/proc/testcgroupbug", O_RDWR);if (fd < 0) {printf("/proc/testcgroupbug do not exist!\n");return -ENOENT;}printf("/proc/testcgrouopbug succeed!\n");testpara para;para.mode = 0;para.sleepsecond = duration;if (__glibc_unlikely((ioctl(fd, IOCTL_SET_MODE, &para)) < 0)) {printf("/proc/testcgroupbug ioctl fail!\n");return -errno;}printf("/proc/testcgroupbug ioctl success!\n");return 0;
}

2.1.3 复现程序用的rwlock_write用户态程序

#include <cstring>
#include <iostream>
#include <csignal>
#include <thread>
#include <chrono>
#include <ctime>
#include <atomic>
#include <cmath>
#include <fstream>
#include <vector>
#include <memory>
#include <map>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/ioctl.h>
#include <linux/types.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/syscall.h>#define DEVICE_NAME "testcgroupbug"
#define IOCTL_SET_MODE _IOW('a', 1, long)typedef struct testpara {int mode;int sleepsecond;
} testpara;#define IOCTL_SET_MODE _IOW('a', 1, long)int main(int argc, char *argv[])
{if (argc != 2) {fprintf(stderr, "Usage: %s <sleep_time_in_seconds>\n", argv[0]);return 1;}int duration = atoi(argv[1]);int fd = open("/proc/testcgroupbug", O_RDWR);if (fd < 0) {printf("/proc/testcgroupbug do not exist!\n");return -ENOENT;}printf("/proc/testcgrouopbug succeed!\n");testpara para;para.mode = 1;para.sleepsecond = duration;if (__glibc_unlikely((ioctl(fd, IOCTL_SET_MODE, &para)) < 0)) {printf("/proc/testcgroupbug ioctl fail!\n");return -errno;}printf("/proc/testcgroupbug ioctl success!\n");return 0;
}

2.1.4 依次启动程序的脚本

再准备一个死循环程序,代码如下,比较简单:

#include <stdio.h>int main()
{while(1);return 1;
}

然后就是一个脚本,来按照如下的时序来依次启动程序:

#!/bin/bashpkill deadlooprmmod testcgroupbug
insmod testcgroupbug.kodmesg -cmkdir /sys/fs/cgroup/test
echo "500000 1000000" > /sys/fs/cgroup/test/cpu.maxtaskset -c 0 ./deadloop &
taskset -c 2 ./deadloop &
taskset -c 3 ./deadloop &
taskset -c 4 ./deadloop &
taskset -c 5 ./deadloop &sleep 3taskset -c 1 ./rwlock_read 5 &pid=$!echo $pid > /sys/fs/cgroup/test/cgroup.procs#sleep 2chrt -f 60 ./rwlock_write 3 &#sleep 2#./rwlock_read 10 &

2.2 复现方法

复现方法比较简单,就是在rt-linux内核上执行上面 2.1.4 里的脚本即可。

要注意的是 2.1.4 里的脚本是针对只有6个cpu的情况,如果cpu的数量较多,请自行根据cpu的数量调整脚本,意思就是把cpu1以外的其他核都跑一个死循环的任务,让cpu 1相对比较闲,这样更容易复现该死锁问题。

三、原理及相应解法

3.1 死锁原理

死锁发生需要依赖一定的时序:

1)进程A在持有了rwlock的读锁之后,被其所在的cgroup cpu给throttle了

2)进程B持有了rwlock的写锁,由于rwlock的机制(一旦有人尝试拿写锁,后续的读者都会被阻塞)

3)一个软timer的任务也需要访问该rwlock的读锁,该软timer的任务这时候访问了该rwlock的读锁,所以只能等在那儿,执行不下去了

4)由于之前进程A已经被cgroup cpu给throttle了,且持有者读锁没有释放,软timer的任务假设是在ktimer内核线程上运行,由于也需要拿读锁且读锁没有释放,所以该ktimer内核线程的当前这个处理timer到期的任务包括后面的处理timer到期的任务都得不到执行

5)而cgroup的period timer是pinned的timer,在一开始创建这个period timer如果绑定在某个核上,那因为pinned的模式就一直得在这个核上运行,所以假设绑定的这个核切好就是使用上面说的读锁而被阻塞的软timer任务所在的核上,那么这个period timer就一直得不到执行。因为另外一个细节是,CONFIG_PREEMPT_RT的系统上该cgroup的period timer设的HRTIMER_MODE_ABS_PINNED和HRTIMER_MODE_REL都没带HARD所以都是在ktimer或ksoftirqd里运行,而不是硬中断处理直接触发执行的

6)另外,如果是mutex而不是rwlock,由于ktimer是一个FIFO 1的进程,根据rt-linux上的优先级继承策略,被cgroup限制执行的进程A如果使用的不是rwlock而是mutex的话,会被临时提高优先级到ktimer的FIFO 1优先级,这样就不死锁了。可是很遗憾,这个例子里,进程A是用的rwlock而不是mutex,由于rwlock允许有多个读者,所以它并不能实施该优先级继承的策略,因为优先级继承的实现针对的是一个持锁人的情况。

3.1.1 代码解释

其实在上面的逻辑描述还是比较清晰的,我们再来跟着看一下复现该bug用到的代码。

先启动的rwlock的读者进程,走到了下面的内核模块里的逻辑:

然后执行rwlock的writer,上图里执行了deadloop_second_special函数,确保有rwlock的writer了之后再启动软hrtimer:

启动软hrtimer并设定PINNED:

在该软hrtimer的callback里使用rwlock进行读:

这时候就可能发生循环依赖的死锁。

3.2 死锁解法

我们有一个相对简单的针对该死锁问题的解法,改动如下:

意思就是在rt-linux下把该cgroup cpu相关的两个period相关的timer都改成和普通内核里的timer的实现方式一样,即使用硬中断响应该时间到期的事件,而不用当前rt-linux里采用的hrtimer的软处理逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java—12 kafka

目录 一、消息队列的优缺点 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ选型对比 适用场景——从公司基础建设力量角度出发 适用场景——从业务场景角度出发 四、基本概念和操作 1. kafka常用术语 2. kafka常用指令 3. 单播消息&a…

14【模块学习】74HC595:使用学习

74HC595 1、74HC595简介2、代码演示2.1、驱动8位流水灯 3、74HC595级联3.1、驱动16位流水灯3.2、驱动8位数码管3.3、驱动8x8点阵屏幕3.4、8x8点阵屏幕滚动显示 1、74HC595简介 在51单片机中IO引脚资源十分的紧缺&#xff0c;所以常常需要使用75HC595芯片进行驱动那些需要占用多…

JAVA后端开发常用的LINUX命令总结

一、Linux常用命令大全&#xff08;2025年最新版&#xff09; 常用 Linux 命令 文件和目录管理&#xff1a; cd&#xff1a;用于切换当前工作目录&#xff0c;如cd /home/user。mkdir&#xff1a;创建新目录&#xff0c;mkdir -p /home/user/mydir可递归创建多级目录。pwd&am…

uniapp-商城-40-shop 购物车 选好了 进行订单确认4 配送方式3 地址编辑

前面说了配送 和地址页面 当地址页面为空或需要添加地址时&#xff0c;需要添加地址。 我的地址页面有个按钮 就是添加地址 点击 添加地址 按钮 后&#xff0c;就会跳转到地址添加的页面 1、添加地址页面 2、添加地址文件夹以及文件的创建 3、添加地址的代码 <template…

现场问题排查-postgresql某表索引损坏导致指定数据无法更新影响卷宗材料上传

问题现象 今天突然被拉进一个群&#xff0c;说某地区友商推送编目结果报错&#xff0c;在我们自己的卷宗系统上传材料也一直转圈&#xff0c;也删除不了案件卷宗&#xff0c;重置模板也没用&#xff0c;只有个别案件有问题。虽然这事儿不属于我负责&#xff0c;但还是抽时间给…

Redis01-基础-入门

零、文章目录 Redis01-基础-入门 1、认识 NoSQL NoSQL 知识请参考&#xff1a;https://blog.csdn.net/liyou123456789/article/details/132612444 2、认识 Redis &#xff08;1&#xff09;简介 Redis&#xff08;Remote Dictionary Server&#xff0c;远程字典服务&…

【嘉立创EDA】如何在更新或转换原理图到PCB时,保留已有布局器件

文章路标👉 :one: 文章解决问题:two: 主题内容:three: 参考方法be end..1️⃣ 文章解决问题 操作环境:嘉立创EDA专业版 V2.2.37 本文使用嘉立创EDA,描述在更新或转换原理图到PCB时,保留已有布局器件的方法。本文将此过程记录,以供有需要的读者参考。 2️⃣ 主题内容 …

03 APQC PROCESS CLASSIFICATION FRAMEWORK (PCF)

APQC流程分类框架&#xff08;APQC Process Classification Framework, PCF&#xff09;最初由美国生产力与质量中心&#xff08;American Productivity & Quality Center, APQC&#xff09;开发&#xff0c;旨在用于跨组织的流程性能基准比较。现在&#xff0c;它也常被用…

分析型数据库入门指南:如何选择适合你的实时分析工具?

一、什么是分析型数据库&#xff1f;为什么需要它&#xff1f; 据Gartner最新报告显示&#xff0c;超过75%的企业现已在关键业务部门部署了专门的分析型数据库&#xff0c;这一比例还在持续增长。 随着数据量呈指数级增长&#xff0c;传统数据库已无法满足复杂分析场景的需求…

body Param Query 三个 不同的入参 分别是什么意思 在前端 要怎么传 这三种不同的参数

在 NestJS 中&#xff0c;Body()、Param() 和 Query() 用于处理不同类型的请求参数。以下是它们的含义及前端传递方式&#xff1a; Body()&#xff1a;请求体参数 • 含义&#xff1a;用于获取请求体中的数据&#xff08;如 POST/PUT 请求中提交的 JSON、表单数据等&#xff09…

神经网络(自己记录)

一、神经网络基础 5分钟-通俗易懂 - 神经网络 反向传播算法&#xff08;手算&#xff09;_哔哩哔哩_bilibili 二、GAT

Redis Slot 槽位分片具体案例

‌键值槽位分配案例‌ 当执行 SET {kaigejava}k1 v1 时&#xff0c;Redis 会提取 {} 内的有效部分 kaigejava&#xff0c;通过 CRC16 算法计算哈希值&#xff0c;再对 16384 取余得到槽位。例如&#xff1a; 若计算结果为 1495&#xff0c;则该键会被分配到槽位 1495 对应的节…

【多模态模型】跨模态智能的核心技术与应用实践

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现&#xff08;CLIP图像-文本检索&#xff09;运行结果验证 三、性能对比测试方法论量化数据对比结果…

final static 中是什么final static联合使用呢

final static 联合使用详解 final 和 static 在 Java 中经常一起使用&#xff0c;主要用来定义类级别的常量。这种组合具有两者的特性&#xff1a; 基本用法 public class Constants {// 典型的 final static 常量定义public static final double PI 3.141592653589793;pub…

1.1 道路结构特征

1.1 道路结构特征 1.城市道路分类 道路网的地位、交通功能、沿线的服务功能。快速路 15 30主干路 15 30次干路 15 20支路 10 20 10(20)沥青路面、水泥混凝土路面、砌块路面沥青路面:沥青混凝土、沥青贯入式、沥青表面处治。沥青混凝土各种等级、沥青贯入式和沥青表面处治支路…

C++如何使用调试器(如GDB、LLDB)进行程序调试保姆级教程(2万字长文)

C++作为一门高性能、接近底层的编程语言,其复杂性和灵活性为开发者提供了强大的能力,同时也带来了更高的调试难度。与一些高级语言不同,C++程序往往直接操作内存,涉及指针、引用、多线程等特性,这些都可能成为错误的温床。例如,一个未初始化的指针可能导致程序崩溃,而一…

vite+vue构建的网站项目localhost:5173打不开

原因&#xff1a;关掉了cmd命令提示符&#xff0c;那个端口就没有被配置上&#xff0c;打开就是这样的。 解决方法&#xff1a;重新在工作目录下打开cmd&#xff0c;输入npm run dev重新启动项目。 重新出现这样的界面说明已经成功启动项目&#xff0c;再次在浏览器中刷新并输入…

自主可控鸿道Intewell工业实时操作系统

鸿道Intewell工业实时操作系统是东土科技旗下科东软件自主研发的新一代智能工业操作系统&#xff0c;以下是相关介绍&#xff1a; 系统架构 -Intewell-C全实时构型&#xff1a;设备上只运行自研RTOS的全实时系统&#xff0c;适用于有功能安全认证需求的实时控制场景&#xf…

将大语言模型(LLM)应用于自动驾驶(ADAS)中的几个方向,及相关论文示例

主要方法集中在如何利用LLM的强大推理能力和语言理解能力来增强自动驾驶系统的感知、决策和规划能力。以下是几种典型的方法和思路&#xff1a; 1. 基于LLM的驾驶决策与规划 方法&#xff1a;将LLM作为驾驶决策的核心模块&#xff0c;利用其强大的推理能力生成驾驶行为或轨迹…

rt-linux下的D状态的堆栈抓取及TASK_RTLOCK_WAIT状态

一、背景 在之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里的 2.1 一节里的代码&#xff0c;我们已经有了一个比较强大的抓取D状态和等IO状态超过阈值的waker和wakee的堆栈状态的内核模块。在之前的博客 增加等IO状态的唤醒堆栈打印及缺页异常导致iowa…