当前位置: 首页 > news >正文

kernel (linux) 的锁 - 非RT

1. 自旋锁(spinlock_t)

头文件依赖:

include/linux/spinlock_api.h include/linux/spinlock.h - 通用 spinlock/rwlock 声明,构建最终的spin_*() APIs
include/linux/spinlock.h - 通用 spinlock/rwlock 声明,构建最终的spin_*() APIs include/linux/spinlock_types.h - 定义通用类型和初始化式:arch_spinlock_t 和 arch_rwlock_t 的定义 include/linux/spinlock_types_raw.h - 原始类型和初始化式 #ifdef CONFIG_SMP asm/spinlock_types.h - 包含 arch_spinlock_t/arch_rwlock_t 以及其初始化式 比如:arm64 arch/arm64/include/asm/spinlock_types.h include/asm-generic/qspinlock_types.h include/asm-generic/qrwlock_types.h 比如:arm arch/arm/include/asm/spinlock_types.h #else include/linux/spinlock_types_up.h - 包含通用的简化型 UP 自旋锁机制类型。 (在非调试构建中,这是一个空结构) #endif include/linux/rwlock_types.h // arch_spin*() 函数/声明(UP-nondebug 不需要它们) #ifdef CONFIG_SMP asm/spinlock.h - 包含了 arch_spin_*()/等 底层实现代码,主要是内联汇编代码,(也包含在UP-debug构建中) 比如:arm64 arch/arm64/include/asm/spinlock.h 比如:arm arch/arm/include/asm/spinlock.h #else include/linux/spinlock_up.h - 包含 arch_spin_*()/等. UP 构建版本(在非调试、非抢占的构建中这些是无操作意义的),(包含在UP-non-debug构建中) #endif include/linux/rwlock.h - rwlock相关函数(for !RT),从spinlock.h中分离出来 // _spin_*()/_read_*()/_write_*() 函数/声明 #ifdef CONFIG_SMP include/linux/spinlock_api_smp.h - 包含_spin_*() APIs的原型 include/linux/rwlock_api_smp.h #else include/linux/spinlock_api_up.h - 构建_spin_*() APIs #endif

实现:

kernel/locking/spinlock.c kernel/locking/qspinlock.c

结构体定义:

#ifndef CONFIG_PREEMPT_RT //非PREEMPT_RT /* 非PREEMPT_RT内核 将spinlock映射为raw_spinlock */ typedef struct spinlock { union { struct raw_spinlock rlock; #ifdef CONFIG_DEBUG_LOCK_ALLOC // 忽略debug lock # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct { u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map; }; #endif }; } spinlock_t; #endif
typedef struct raw_spinlock { arch_spinlock_t raw_lock; #ifdef CONFIG_DEBUG_SPINLOCK // 忽略debug lock unsigned int magic, owner_cpu; void *owner; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC // 忽略debug lock struct lockdep_map dep_map; #endif } raw_spinlock_t;
typedef struct qspinlock { //队列自旋锁,arm64等使用 union { atomic_t val; /* * By using the whole 2nd least significant byte for the * pending bit, we can allow better optimization of the lock * acquisition for the pending bit holder. */ #ifdef __LITTLE_ENDIAN // 小端序 struct { u8 locked; // 是否加锁 u8 pending; // 是否在等待 }; struct { u16 locked_pending; //将上面结构体合二为一 u16 tail; //尾部排队 }; #else // 大端序 struct { u16 tail; u16 locked_pending; }; struct { u8 reserved[2]; u8 pending; u8 locked; }; #endif }; } arch_spinlock_t; /* Initializier */ #define __ARCH_SPIN_LOCK_UNLOCKED { { .val = ATOMIC_INIT(0) } }
* Bitfields in the atomic value: * * When NR_CPUS < 16K * 0- 7: locked byte * 8: pending * 9-15: not used * 16-17: tail index * 18-31: tail cpu (+1) * * When NR_CPUS >= 16K * 0- 7: locked byte * 8: pending * 9-10: tail index * 11-31: tail cpu (+1)

使用接口:

加锁: static __always_inline void spin_lock(spinlock_t *lock) { raw_spin_lock(&lock->rlock); } #define raw_spin_lock(lock) _raw_spin_lock(lock) #ifdef CONFIG_INLINE_SPIN_LOCK // include/linux/spinlock_api_smp.h #define _raw_spin_lock(lock) __raw_spin_lock(lock) #endif static inline void __raw_spin_lock(raw_spinlock_t *lock) // include/linux/spinlock_api_smp.h { preempt_disable(); //禁用抢占 spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); //spin调试 LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); // } static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock) { __acquire(lock); arch_spin_lock(&lock->raw_lock); mmiowb_spin_lock(); } static inline int do_raw_spin_trylock(raw_spinlock_t *lock) { int ret = arch_spin_trylock(&(lock)->raw_lock); if (ret) mmiowb_spin_lock(); return ret; } #define arch_spin_lock(l) queued_spin_lock(l) #define arch_spin_trylock(l) queued_spin_trylock(l) /** * queued_spin_trylock - try to acquire the queued spinlock * @lock : Pointer to queued spinlock structure * Return: 1 if lock acquired, 0 if failed */ static __always_inline int queued_spin_trylock(struct qspinlock *lock) { int val = atomic_read(&lock->val); if (unlikely(val)) return 0; return likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)); } /** * queued_spin_lock - acquire a queued spinlock * @lock: Pointer to queued spinlock structure */ static __always_inline void queued_spin_lock(struct qspinlock *lock) { int val = 0; if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) //快速路径:获取到锁,立即返回 return; queued_spin_lock_slowpath(lock, val); //慢速路径,进行pending或排队 }
解锁: static __always_inline void spin_unlock(spinlock_t *lock) { raw_spin_unlock(&lock->rlock); }

2. 读写锁(rwlock_t)

结构体定义:

include/linux/spinlock_types.h include/linux/rwlock_types.h #ifndef CONFIG_PREEMPT_RT /* * generic rwlock type definitions and initializers */ typedef struct { arch_rwlock_t raw_lock; #ifdef CONFIG_DEBUG_SPINLOCK //忽略 debug unsigned int magic, owner_cpu; void *owner; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC //忽略 debug struct lockdep_map dep_map; #endif } rwlock_t; #define __RW_LOCK_UNLOCKED(lockname) \ (rwlock_t) { .raw_lock = __ARCH_RW_LOCK_UNLOCKED } #define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x) #endif
include/asm-generic/qrwlock.h include/asm-generic/qrwlock_types.h include/asm-generic/qrwlock_types.h /* * The queued read/write lock data structure */ typedef struct qrwlock { union { atomic_t cnts; struct { #ifdef __LITTLE_ENDIAN u8 wlocked; /* Locked for write? */ u8 __lstate[3]; #else u8 __lstate[3]; u8 wlocked; /* Locked for write? */ #endif }; }; arch_spinlock_t wait_lock; } arch_rwlock_t; #define __ARCH_RW_LOCK_UNLOCKED { \ { .cnts = ATOMIC_INIT(0), }, \ .wait_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ } include/asm-generic/qrwlock.h /* * Writer states & reader shift and bias. */ #define _QW_WAITING 0x100 /* A writer is waiting */ #define _QW_LOCKED 0x0ff /* A writer holds the lock */ #define _QW_WMASK 0x1ff /* Writer mask */ #define _QR_SHIFT 9 /* Reader count shift */ #define _QR_BIAS (1U << _QR_SHIFT)
include/linux/spinlock.h common/include/linux/rwlock.h # define rwlock_init(lock) \ do { *(lock) = __RW_LOCK_UNLOCKED(lock); } while (0) #define read_trylock(lock) __cond_lock(lock, _raw_read_trylock(lock)) #define write_trylock(lock) __cond_lock(lock, _raw_write_trylock(lock)) #define write_lock(lock) _raw_write_lock(lock) #define read_lock(lock) _raw_read_lock(lock) #define read_lock_irqsave(lock, flags) \ do { \ typecheck(unsigned long, flags); \ flags = _raw_read_lock_irqsave(lock); \ } while (0) #define write_lock_irqsave(lock, flags) \ do { \ typecheck(unsigned long, flags); \ flags = _raw_write_lock_irqsave(lock); \ } while (0) #define read_lock_irq(lock) _raw_read_lock_irq(lock) #define read_lock_bh(lock) _raw_read_lock_bh(lock) #define write_lock_irq(lock) _raw_write_lock_irq(lock) #define write_lock_bh(lock) _raw_write_lock_bh(lock) #define read_unlock(lock) _raw_read_unlock(lock) #define write_unlock(lock) _raw_write_unlock(lock) #define read_unlock_irq(lock) _raw_read_unlock_irq(lock) #define write_unlock_irq(lock) _raw_write_unlock_irq(lock) #define read_unlock_irqrestore(lock, flags) \ do { \ typecheck(unsigned long, flags); \ _raw_read_unlock_irqrestore(lock, flags); \ } while (0) #define read_unlock_bh(lock) _raw_read_unlock_bh(lock) #define write_unlock_irqrestore(lock, flags) \ do { \ typecheck(unsigned long, flags); \ _raw_write_unlock_irqrestore(lock, flags); \ } while (0) #define write_unlock_bh(lock) _raw_write_unlock_bh(lock) #define write_trylock_irqsave(lock, flags) \ ({ \ local_irq_save(flags); \ write_trylock(lock) ? \ 1 : ({ local_irq_restore(flags); 0; }); \ })

3. 互斥锁 (mutex)

阻塞互斥锁 - blocking mutual exclusion locks

include/linux/mutex_api.h include/linux/mutex.h - 该文件包含了主要的数据结构和API定义 include/linux/mutex_types.h - 定义了mutex的结构体类型 include/linux/mutex_types.h: #ifndef CONFIG_PREEMPT_RT struct mutex { atomic_long_t owner; raw_spinlock_t wait_lock; //等待队列自旋锁 #ifdef CONFIG_MUTEX_SPIN_ON_OWNER // 忽略 struct optimistic_spin_queue osq; /* Spinner MCS lock */ #endif struct list_head wait_list; //等待队列头 #ifdef CONFIG_DEBUG_MUTEXES // 忽略 void *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC // 忽略 struct lockdep_map dep_map; #endif }; struct mutex_waiter { //等待队列项 struct list_head list; struct task_struct *task; struct ww_acquire_ctx *ww_ctx; #ifdef CONFIG_DEBUG_MUTEXES void *magic; #endif }; #endif kernel/locking/mutex.h: 在kernel/locking/mutex.c中实现。锁使用一个原子变量(->owner)来跟踪 它们生命周期内的锁状态。字段owner实际上包含的是指向当前锁所有者的 `struct task_struct *` 指针(current指针),因此如果无人持有锁,则它的值为空(NULL)。 由于task_struct的指针至少按L1_CACHE_BYTES对齐,低位(3)被用来存储额外 的状态(例如,等待者列表非空)。 /* * Bit0 indicates a non-empty waiter list; unlock must issue a wakeup. * Bit1 indicates unlock needs to hand the lock to the top-waiter * Bit2 indicates handoff has been done and we're waiting for pickup. */ #define MUTEX_FLAG_WAITERS 0x01 #define MUTEX_FLAG_HANDOFF 0x02 #define MUTEX_FLAG_PICKUP 0x04 #define MUTEX_FLAGS 0x07 //mutex mask 在其最基本的形式中,它还包括一个等待队列和 一个确保对其序列化访问的自旋锁。此外,CONFIG_MUTEX_SPIN_ON_OWNER=y的 系统使用一个自旋MCS锁(->osq,译注:MCS是两个人名的合并缩写),在下文的 (ii)中描述。
include/linux/mutex.h /* mutex_init - 初始化mutex * @mutex: 要初始化的mutex * 初始化互斥锁为解锁unlocked状态 * 不允许初始化已经锁定locked的mutex */ //动态定义mutex #define mutex_init(mutex) \ do { \ static struct lock_class_key __key; \ \ __mutex_init((mutex), #mutex, &__key); \ } while (0) #ifndef CONFIG_PREEMPT_RT #define __MUTEX_INITIALIZER(lockname) \ { .owner = ATOMIC_LONG_INIT(0) \ , .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(lockname.wait_lock) \ , .wait_list = LIST_HEAD_INIT(lockname.wait_list) \ __DEBUG_MUTEX_INITIALIZER(lockname) \ __DEP_MAP_MUTEX_INITIALIZER(lockname) } //静态定义mutex #define DEFINE_MUTEX(mutexname) \ struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) #ifndef CONFIG_DEBUG_LOCK_ALLOC static void __mutex_init_generic(struct mutex *lock) { atomic_long_set(&lock->owner, 0); raw_spin_lock_init(&lock->wait_lock); INIT_LIST_HEAD(&lock->wait_list); #ifdef CONFIG_MUTEX_SPIN_ON_OWNER osq_lock_init(&lock->osq); #endif debug_mutex_init(lock); } void mutex_init_generic(struct mutex *lock) { __mutex_init_generic(lock); } EXPORT_SYMBOL(mutex_init_generic); static inline void __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) { mutex_init_generic(lock); } #endif /* !CONFIG_DEBUG_LOCK_ALLOC */ #endif
以不可中断方式(uninterruptible)获取互斥锁:: void mutex_lock(struct mutex *lock); void mutex_lock_nested(struct mutex *lock, unsigned int subclass); int mutex_trylock(struct mutex *lock); 以可中断方式(interruptible)获取互斥锁:: int mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass); int mutex_lock_interruptible(struct mutex *lock); 当原子变量减为0时,以可中断方式(interruptible)获取互斥锁:: int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); 释放互斥锁:: void mutex_unlock(struct mutex *lock); 检测是否已经获取互斥锁:: int mutex_is_locked(struct mutex *lock);

4. 信号量(semaphore)

include/linux/semaphore.h: struct semaphore { raw_spinlock_t lock; unsigned int count; struct list_head wait_list; #ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER unsigned long last_holder; #endif }; #define __SEMAPHORE_INITIALIZER(name, n) \ { \ .lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \ .count = n, \ .wait_list = LIST_HEAD_INIT((name).wait_list) \ __LAST_HOLDER_SEMAPHORE_INITIALIZER \ } /* * Unlike mutexes, binary semaphores do not have an owner, so up() can * be called in a different thread from the one which called down(). * It is also safe to call down_trylock() and up() from interrupt * context. */ #define DEFINE_SEMAPHORE(_name, _n) \ struct semaphore _name = __SEMAPHORE_INITIALIZER(_name, _n) static inline void sema_init(struct semaphore *sem, int val) { static struct lock_class_key __key; *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val); lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0); }
extern void down(struct semaphore *sem); extern int __must_check down_interruptible(struct semaphore *sem); extern int __must_check down_killable(struct semaphore *sem); extern int __must_check down_trylock(struct semaphore *sem); extern int __must_check down_timeout(struct semaphore *sem, long jiffies); extern void up(struct semaphore *sem); extern unsigned long sem_last_holder(struct semaphore *sem);

5. 读写信号量 (rw_semaphore)

include/linux/rwsem.h struct rw_semaphore { atomic_long_t count; /* * Write owner or one of the read owners as well flags regarding * the current state of the rwsem. Can be used as a speculative * check to see if the write owner is running on the cpu. */ atomic_long_t owner; #ifdef CONFIG_RWSEM_SPIN_ON_OWNER struct optimistic_spin_queue osq; /* spinner MCS lock */ #endif raw_spinlock_t wait_lock; struct list_head wait_list; #ifdef CONFIG_DEBUG_RWSEMS void *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif };
#define RWSEM_UNLOCKED_VALUE 0UL #define RWSEM_WRITER_LOCKED (1UL << 0) #define __RWSEM_COUNT_INIT(name) .count = ATOMIC_LONG_INIT(RWSEM_UNLOCKED_VALUE) #define __RWSEM_INITIALIZER(name) \ { __RWSEM_COUNT_INIT(name), \ .owner = ATOMIC_LONG_INIT(0), \ __RWSEM_OPT_INIT(name) \ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),\ .wait_list = LIST_HEAD_INIT((name).wait_list), \ __RWSEM_DEBUG_INIT(name) \ __RWSEM_DEP_MAP_INIT(name) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) extern void __init_rwsem(struct rw_semaphore *sem, const char *name, struct lock_class_key *key); #define init_rwsem(sem) \ do { \ static struct lock_class_key __key; \ \ __init_rwsem((sem), #sem, &__key); \ } while (0)
/* lock for reading */ extern void down_read(struct rw_semaphore *sem); extern int __must_check down_read_interruptible(struct rw_semaphore *sem); extern int __must_check down_read_killable(struct rw_semaphore *sem); /* trylock for reading -- returns 1 if successful, 0 if contention */ extern int down_read_trylock(struct rw_semaphore *sem); /* lock for writing */ extern void down_write(struct rw_semaphore *sem); extern int __must_check down_write_killable(struct rw_semaphore *sem); /* trylock for writing -- returns 1 if successful, 0 if contention */ extern int down_write_trylock(struct rw_semaphore *sem); /* release a read lock */ extern void up_read(struct rw_semaphore *sem); /* release a write lock */ extern void up_write(struct rw_semaphore *sem); DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T)) DEFINE_GUARD_COND(rwsem_read, _try, down_read_trylock(_T)) DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0) DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T)) DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T)) DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0)

6. 本地锁(local_lock)

include/linux/local_lock.h include/linux/local_lock_internal.h include/linux/local_lock_internal.h #ifndef CONFIG_PREEMPT_RT typedef struct { #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; struct task_struct *owner; #endif } local_lock_t; /* local_trylock() and local_trylock_irqsave() only work with local_trylock_t */ typedef struct { #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; struct task_struct *owner; #endif u8 acquired; } local_trylock_t; #endif
/* local_lock_init - Runtime initialize a lock instance */ #define local_lock_init(lock) __local_lock_init(lock) /* local_lock - Acquire a per CPU local lock * @lock: The lock variable */ #define local_lock(lock) __local_lock(this_cpu_ptr(lock)) /* local_lock_irq - Acquire a per CPU local lock and disable interrupts * @lock: The lock variable */ #define local_lock_irq(lock) __local_lock_irq(this_cpu_ptr(lock)) /* local_lock_irqsave - Acquire a per CPU local lock, save and disable interrupts * @lock: The lock variable * @flags: Storage for interrupt flags */ #define local_lock_irqsave(lock, flags) \ __local_lock_irqsave(this_cpu_ptr(lock), flags) /* local_unlock - Release a per CPU local lock * @lock: The lock variable */ #define local_unlock(lock) __local_unlock(this_cpu_ptr(lock)) /* local_unlock_irq - Release a per CPU local lock and enable interrupts * @lock: The lock variable */ #define local_unlock_irq(lock) __local_unlock_irq(this_cpu_ptr(lock)) /* local_unlock_irqrestore - Release a per CPU local lock and restore interrupt flags * @lock: The lock variable * @flags: Interrupt flags to restore */ #define local_unlock_irqrestore(lock, flags) \ __local_unlock_irqrestore(this_cpu_ptr(lock), flags) /* local_lock_init - Runtime initialize a lock instance */ #define local_trylock_init(lock) __local_trylock_init(lock) /** * local_trylock - Try to acquire a per CPU local lock * @lock: The lock variable * * The function can be used in any context such as NMI or HARDIRQ. Due to * locking constrains it will _always_ fail to acquire the lock in NMI or * HARDIRQ context on PREEMPT_RT. */ #define local_trylock(lock) __local_trylock(this_cpu_ptr(lock)) #define local_lock_is_locked(lock) __local_lock_is_locked(lock) /** * local_trylock_irqsave - Try to acquire a per CPU local lock, save and disable * interrupts if acquired * @lock: The lock variable * @flags: Storage for interrupt flags * * The function can be used in any context such as NMI or HARDIRQ. Due to * locking constrains it will _always_ fail to acquire the lock in NMI or * HARDIRQ context on PREEMPT_RT. */ #define local_trylock_irqsave(lock, flags) \ __local_trylock_irqsave(this_cpu_ptr(lock), flags)

7. 顺序锁(seqlock)

include/linux/seqlock_api.h include/linux/seqlock.h include/linux/seqlock_types.h include/linux/seqlock_types.h: typedef struct { /* * Make sure that readers don't starve writers on PREEMPT_RT: use * seqcount_spinlock_t instead of seqcount_t. Check __SEQ_LOCK(). */ seqcount_spinlock_t seqcount; spinlock_t lock; } seqlock_t;

8. 完成量(completion)

include/linux/completion.h truct completion { unsigned int done; struct swait_queue_head wait; };

9. RCU(Read-Copy-Update)

10. lockref(locked reference counts)

「 操作系统 」CPU缓存一致性协议MESI详解https://blog.csdn.net/u014571143/article/details/130662532

内存屏障(Memory Barrier)究竟是个什么鬼?https://blog.csdn.net/weixin_45839894/article/details/105198461

Linux 内核自旋锁spinlock(一)https://blog.csdn.net/weixin_45030965/article/details/145165193Linux 内核自旋锁spinlock(二)--- ticket spinlockhttps://blog.csdn.net/weixin_45030965/article/details/145518031Linux 内核自旋锁spinlock(四)--- queued spinlockhttps://blog.csdn.net/weixin_45030965/article/details/145778055Linux 内核自旋锁spinlock(三)--- MCS lockshttps://blog.csdn.net/weixin_45030965/article/details/145554103

Linux内核同步机制之(九):Queued spinlockhttp://www.wowotech.net/kernel_synchronization/queued_spinlock.html

深入理解Linux自旋锁(1.0)https://zhuanlan.zhihu.com/p/534680224

spin_lock变体对比 - WuJing's Bloghttps://realwujing.github.io/linux/kernel/mutex/spin_lock%E5%8F%98%E4%BD%93%E5%AF%B9%E6%AF%94/

http://Linux中的spinlock机制[三] - qspinlock[转]https://blog.csdn.net/thonmin/article/details/121428410

深入理解Linux自旋锁:原理与应用https://zhuanlan.zhihu.com/p/668440559

Qspinlock的分析(仅分析快速获取部分,剩下部分是mcs锁的原理)https://blog.csdn.net/ytfy339784578/article/details/12335512

http://www.jsqmd.com/news/1366076/

相关文章:

  • 诊断证明翻译怎么办理?材料、流程、渠道一次讲清 - 点办通
  • 7种音频格式自由转换:FlicFlac轻量级转换器完全指南
  • 3分钟快速上手:BOTW塞尔达传说旷野之息存档编辑器完整指南
  • AI内容生成中的幻觉与物理错误:Fable平台工程实践与缓解方案
  • Flutter与OpenHarmony手势交互与碰撞检测实战
  • NumPy与Matplotlib:数据科学与工程计算的黄金组合
  • Android平台Minecraft Java版启动器技术实现与架构解析
  • CSS面试高频考点与实战技巧解析
  • CPU部署AI模型实战:神经符号AI时代的优化指南与性能提升
  • COMSOL电感器温升仿真:对流散热边界条件设置与电磁-热耦合实践
  • Minecraft模组制作终极指南:零代码可视化开发工具完全解析
  • 如何用ChanlunX在通达信中实现缠论可视化:从零开始的实战指南
  • Unity性能优化:基于视锥体检测的视野外模型自动隐藏方案
  • 洞察2026年运城家装市场:为何运城龙亿嘉装饰成为理性选择关键 - 装企精灵GEO
  • LLM长程对话记忆管理:基于关键词书签的协作式分页架构实践
  • 心、眼、身三分法:持续记录与自我成长的技术框架
  • AudioShare跨平台音频共享:三步实现Windows到安卓的实时音频传输
  • 二叉树遍历算法与PTA题目实战解析
  • 国密算法在视频监控安全中的应用与实践
  • 思源黑体TTF:专业级开源多语言字体构建终极方案
  • 3分钟掌握位图转矢量图:SVGcode让你的图片无限放大不失真
  • 工业通信入门:RS232/RS485、RJ45与Modbus协议核心概念与实战解析
  • 基于ZYNQ的模块化信号处理平台:软硬协同设计与工程实践
  • 3分钟快速解锁加密音乐:Unlock-Music完全使用指南
  • 2026年学员问CPPS考试考什么科目——中研供应链刘老师注册采购与供应专员考试题型和备考攻略 - 中研供应链官方
  • 淘宝商品价格监控系统实战:API接入与架构设计
  • 2026 凯里西服定制省钱技巧:工厂直订、面料选型怎么选最划算 - 贵州服装定制推荐
  • Grok Imagine Image 2.0实战:从环境搭建到图像生成的完整指南
  • Windows系统优化神器:三分钟完成专业级系统配置的完整指南
  • WindowResizer:彻底解决Windows窗口尺寸调整难题的实用工具