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.pswp.cn/bicheng/78687.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78687.shtml
英文地址,請注明出處:http://en.pswp.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…