非阻塞型同步 (Non-blocking Synchronization) 简介
如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步。同步可分为阻塞型同步(Blocking Synchronization)和非阻塞型同步( Non-blocking Synchronization)。
阻塞型同步是指当一个线程到达临界区时,因另外一个线程已经持有访问该共享数据的锁,从而不能获取锁资源而阻塞,直到另外一个线程释放锁。常见的同步原语有 mutex、semaphore 等。如果同步方案采用不当,就会造成死锁(deadlock),活锁(livelock)和优先级反转(priority inversion),以及效率低下等现象。
为了降低风险程度和提高程序运行效率,业界提出了不采用锁的同步方案,依照这种设计思路设计的算法称为非阻塞型算法,其本质特征就是停止一个线程的执行不会阻碍系统中其他执行实体的运行。
当今比较流行的 Non-blocking Synchronization 实现方案有三种:
Wait-free
Wait-free 是指任意线程的任何操作都可以在有限步之内结束,而不用关心其它线程的执行速度。 Wait-free 是基于 per-thread 的,可以认为是 starvation-free 的。非常遗憾的是实际情况并非如此,采用 Wait-free 的程序并不能保证 starvation-free,同时内存消耗也随线程数量而线性增长。目前只有极少数的非阻塞算法实现了这一点。
Lock-free
Lock-Free 是指能够确保执行它的所有线程中至少有一个能够继续往下执行。由于每个线程不是 starvation-free 的,即有些线程可能会被任意地延迟,然而在每一步都至少有一个线程能够往下执行,因此系统作为一个整体是在持续执行的,可以认为是 system-wide 的。所有 Wait-free 的算法都是 Lock-Free 的。
Obstruction-free
Obstruction-free 是指在任何时间点,一个孤立运行线程的每一个操作可以在有限步之内结束。只要没有竞争,线程就可以持续运行。一旦共享数据被修改,Obstruction-free 要求中止已经完成的部分操作,并进行回滚。 所有 Lock-Free 的算法都是 Obstruction-free 的。
综上所述,不难得出 Obstruction-free 是 Non-blocking synchronization 中性能最差的,而 Wait-free 性能是最好的,但实现难度也是最大的,因此 Lock-free 算法开始被重视,并广泛运用于当今正在运行的程序中,比如 linux 内核。
一般采用原子级的 read-modify-write 原语来实现 Lock-Free 算法,其中 LL 和 SC 是 Lock-Free 理论研究领域的理想原语,但实现这些原语需要 CPU 指令的支持,非常遗憾的是目前没有任何 CPU 直接实现了 SC 原语。根据此理论,业界在原子操作的基础上提出了著名的 CAS(Compare - And - Swap)操作来实现 Lock-Free 算法,Intel 实现了一条类似该操作的指令:cmpxchg8。
CAS 原语负责将某处内存地址的值(1 个字节)与一个期望值进行比较,如果相等,则将该内存地址处的值替换为新值,CAS 操作伪码描述如下:
清单 1. CAS 伪码
Bool CAS(T* addr, T expected, T newValue)
{
if( *addr == expected )
{
*addr = newValue;
return true;
}
else
return false;
}
在实际开发过程中,利用 CAS 进行同步,代码如下所示:
清单 2. CAS 实际操作
do{
备份旧数据;
基于旧数据构造新数据;
}while(!CAS( 内存地址,备份的旧数据,新数据 ))
就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后重新执行刚才的操作。容易看出 CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的 commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。
回页首
加锁的层级
根据复杂程度、加锁粒度及运行速度,可以得出如下图所示的锁层级:
图 1. 加锁层级
其中标注为红色字体的方案为 Blocking synchronization,黑色字体为 Non-blocking synchronization。Lock-based 和 Lockless-based 两者之间的区别仅仅是加锁粒度的不同。图中最底层的方案就是大家经常使用的 mutex 和 semaphore 等方案,代码复杂度低,但运行效率也最低。
回页首
Linux 内核中的无锁分析
Linux 内核可能是当今最大最复杂的并行程序之一,它的并行主要来至于中断、内核抢占及 SMP 等。内核设计者们为了不断提高 Linux 内核的效率,从全局着眼,逐步废弃了大内核锁来降低锁的粒度;从细处下手,不断对局部代码进行优化,用无锁编程替代基于锁的方案,如 seqlock 及 RCU 等;不断减少锁冲突程度、降低等待时间,如 Double-checked locking 和原子锁等。
内核无锁第一层级 — 少锁
无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,此时就可以利用 Double-checked Locking 模式来减少锁竞争和加锁载荷。目前 Double-checked Locking 已经广泛应用于单例 (Singleton) 模式中。内核设计者基于此思想,巧妙的将 Double-checked Locking 方法运用于内核代码中。
当一个进程已经僵死,即进程处于 TASK_ZOMBIE 状态,如果父进程调用 waitpid() 系统调用时,父进程需要为子进程做一些清理性的工作,代码如下所示:
清单 3. 少锁操作
984 static int wait_task_zombie(task_t *p, int noreap,
985 struct siginfo __user *infop,
986 int __user *stat_addr, struct rusage __user *ru)
987 {
……
1103 if (p->real_parent != p->parent) {
1104 write_lock_irq(&tasklist_lock);
1105 /* Double-check with lock held. */
1106 if (p->real_parent != p->parent) {
1107 __ptrace_unlink(p);
1108 // TODO: is this safe?
1109 p->exit_state = EXIT_ZOMBIE;
……
1120 }
1121 write_unlock_irq(&tasklist_lock);
1122 }
……
1127 }
如果将 write_lock_irq 放置于 1103 行之前,锁的范围过大,锁的负载也会加重,影响效率;如果将加锁的代码放到判断里面,且没有 1106 行的代码,程序会正确吗?在单核情况下是正确的,但在双核情况下问题就出现了。一个非主进程在一个 CPU 上运行,正准备调用 exit 退出,此时主进程在另外一个 CPU 上运行,在子进程调用 release_task 函数之前调用上述代码。子进程在 exit_notify 函数中,先持有读写锁 tasklist_lock,调用 forget_original_parent。主进程运行到 1104 处,由于此时子进程先持有该锁,所以父进程只好等待。在 forget_original_parent 函数中,如果该子进程还有子进程,则会调用 reparent_thread(),将执行 p->parent = p->real_parent; 语句,导致两者相等,等非主进程释放读写锁 tasklist_lock 时,另外一个 CPU 上的主进程被唤醒,一旦开始执行,继续运行将会导致 bug。
严格的说,Double-checked locking 不属于无锁编程的范畴,但由原来的每次加锁访问到大多数情况下无须加锁,就是一个巨大的进步。同时从这里也可以看出一点端倪,内核开发者为了降低锁冲突率,减少等待时间,提高运行效率,一直在持续不断的进行改进。
内核无锁第二层级 — 原子锁
原子操作可以保证指令以原子的方式执行——执行过程不被打断。内核提供了两组原子操作接口:一组针对于整数进行操作,另外一组针对于单独的位进行操作。内核中的原子操作通常是内联函数,一般是通过内嵌汇编指令来完成。对于一些简单的需求,例如全局统计、引用计数等等,可以归结为是对整数的原子计算。
内核无锁第三层级 — Lock-free
1. Lock-free 应用场景一 —— Spin Lock
Spin Lock 是一种轻量级的同步方法,一种非阻塞锁。当 lock 操作被阻塞时,并不是把自己挂到一个等待队列,而是死循环 CPU 空转等待其他线程释放锁。 Spin lock 锁实现代码如下:
清单 4. spin lock 实现代码
static inline void __preempt_spin_lock(spinlock_t *lock)
{
……
do {
preempt_enable();
while (spin_is_locked(lock))
cpu_relax();
preempt_disable();
} while (!_raw_spin_trylock(lock));
}
static inline int _raw_spin_trylock(spinlock_t *lock)
{
char oldval;
__asm__ __volatile__(
"xchgb %b0,%1"
:"=q" (oldval), "=m" (lock->lock)
:"0" (0) : "memory");
return oldval > 0;
}
汇编语言指令 xchgb 原子性的交换 8 位 oldval( 存 0) 和 lock->lock 的值,如果 oldval 为 1(lock 初始值为 1),则获取锁成功,反之,则继续循环,接着 relax 休息一会儿,然后继续周而复始,直到成功。
对于应用程序来说,希望任何时候都能获取到锁,也就是期望 lock->lock 为 1,那么用 CAS 原语来描述 _raw_spin_trylock(lock) 就是 CAS(lock->lock,1,0);
如果同步操作总是能在数条指令内完成,那么使用 Spin Lock 会比传统的 mutex lock 快一个数量级。Spin Lock 多用于多核系统中,适合于锁持有时间小于将一个线程阻塞和唤醒所需时间的场合。
pthread 库已经提供了对 spin lock 的支持,所以用户态程序也能很方便的使用 spin lock 了,需要包含 pthread.h 。在某些场景下,pthread_spin_lock 效率是 pthread_mutex_lock 效率的一倍多。美中不足的是,内核实现了读写 spin lock 锁,但 pthread 未能实现。
2. Lock -free 应用场景二 —— Seqlock
手表最主要最常用的功能是读时间,而不是校正时间,一旦后者成了最常用的功能,消费者肯定不会买账。计算机的时钟也是这个功能,修改时间是小概率事件,而读时间是经常发生的行为。以下代码摘自 2.4.34 内核:
清单 5. 2.4.34 seqlock 实现代码
443 void do_gettimeofday(struct timeval *tv)
444 {
……
448 read_lock_irqsave(&xtime_lock, flags);
……
455 sec = xtime.tv_sec;
456 usec += xtime.tv_usec;
457 read_unlock_irqrestore(&xtime_lock, flags);
……
466 }
468 void do_settimeofday(struct timeval *tv)
469 {
470 write_lock_irq(&xtime_lock);
……
490 write_unlock_irq(&xtime_lock);
491 }
不难发现获取时间和修改时间采用的是 spin lock 读写锁,读锁和写锁具有相同的优先级,只要读持有锁,写锁就必须等待,反之亦然。
Linux 2.6 内核中引入一种新型锁——顺序锁 (seqlock),它与 spin lock 读写锁非常相似,只是它为写者赋予了较高的优先级。也就是说,即使读者正在读的时候也允许写者继续运行。当存在多个读者和少数写者共享一把锁时,seqlock 便有了用武之地,因为 seqlock 对写者更有利,只要没有其他写者,写锁总能获取成功。根据 lock-free 和时钟功能的思想,内核开发者在 2.6 内核中,将上述读写锁修改成了顺序锁 seqlock,代码如下:
清单 6. 2.6.10 seqlock 实现代码
static inline unsigned read_seqbegin(const seqlock_t *sl)
{
unsigned ret = sl->sequence;
smp_rmb();
return ret;
}
static inline int read_seqretry(const seqlock_t *sl, unsigned iv)
{
smp_rmb();
return (iv & 1) | (sl->sequence ^ iv);
}
static inline void write_seqlock(seqlock_t *sl)
{
spin_lock(&sl->lock);
++sl->sequence;
smp_wmb();
}
void do_gettimeofday(struct timeval *tv)
{
unsigned long seq;
unsigned long usec, sec;
unsigned long max_ntp_tick;
……
do {
unsigned long lost;
seq = read_seqbegin(&xtime_lock);
……
sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry(&xtime_lock, seq));
……
tv->tv_sec = sec;
tv->tv_usec = usec;
}
int do_settimeofday(struct timespec *tv)
{
……
write_seqlock_irq(&xtime_lock);
……
write_sequnlock_irq(&xtime_lock);
clock_was_set();
return 0;
}
Seqlock 实现原理是依赖一个序列计数器,当写者写入数据时,会得到一把锁,并且将序列值加 1。当读者读取数据之前和之后,该序列号都会被读取,如果读取的序列号值都相同,则表明写没有发生。反之,表明发生过写事件,则放弃已进行的操作,重新循环一次,直至成功。不难看出,do_gettimeofday 函数里面的 while 循环和接下来的两行赋值操作就是 CAS 操作。
采用顺序锁 seqlock 好处就是写者永远不会等待,缺点就是有些时候读者不得不反复多次读相同的数据直到它获得有效的副本。当要保护的临界区很小,很简单,频繁读取而写入很少发生(WRRM--- Write Rarely Read Mostly)且必须快速时,就可以使用 seqlock。但 seqlock 不能保护包含有指针的数据结构,因为当写者修改数据结构时,读者可能会访问一个无效的指针。
3. Lock -free 应用场景三 —— RCU
在 2.6 内核中,开发者还引入了一种新的无锁机制 -RCU(Read-Copy-Update),允许多个读者和写者并发执行。RCU 技术的核心是写操作分为写和更新两步,允许读操作在任何时候无阻碍的运行,换句话说,就是通过延迟写来提高同步性能。RCU 主要应用于 WRRM 场景,但它对可保护的数据结构做了一些限定:RCU 只保护被动态分配并通过指针引用的数据结构,同时读写控制路径不能有睡眠。以下数组动态增长代码摘自 2.4.34 内核:
清单 7. 2.4.34 RCU 实现代码
其中 ipc_lock 是读者,grow_ary 是写者,不论是读或者写,都需要加 spin lock 对被保护的数据结构进行访问。改变数组大小是小概率事件,而读取是大概率事件,同时被保护的数据结构是指针,满足 RCU 运用场景。以下代码摘自 2.6.10 内核:
清单 8. 2.6.10 RCU 实现代码
#define rcu_read_lock() preempt_disable()
#define rcu_read_unlock() preempt_enable()
#define rcu_assign_pointer(p, v) ({ \
smp_wmb(); \
(p) = (v); \
})
struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
{
……
rcu_read_lock();
entries = rcu_dereference(ids->entries);
if(lid >= entries->size) {
rcu_read_unlock();
return NULL;
}
out = entries->p[lid];
if(out == NULL) {
rcu_read_unlock();
return NULL;
}
……
return out;
}
static int grow_ary(struct ipc_ids* ids, int newsize)
{
struct ipc_id_ary* new;
struct ipc_id_ary* old;
……
new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize +
sizeof(struct ipc_id_ary));
if(new == NULL)
return size;
new->size = newsize;
memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size
+sizeof(struct ipc_id_ary));
for(i=size;i<newsize;i++) {
new->p[i] = NULL;
}
old = ids->entries;
/*
* Use rcu_assign_pointer() to make sure the memcpyed contents
* of the new array are visible before the new array becomes visible.
*/
rcu_assign_pointer(ids->entries, new);
ipc_rcu_putref(old);
return newsize;
}
纵观整个流程,写者除内核屏障外,几乎没有一把锁。当写者需要更新数据结构时,首先复制该数据结构,申请 new 内存,然后对副本进行修改,调用 memcpy 将原数组的内容拷贝到 new 中,同时对扩大的那部分赋新值,修改完毕后,写者调用 rcu_assign_pointer 修改相关数据结构的指针,使之指向被修改后的新副本,整个写操作一气呵成,其中修改指针值的操作属于原子操作。在数据结构被写者修改后,需要调用内存屏障 smp_wmb,让其他 CPU 知晓已更新的指针值,否则会导致 SMP 环境下的 bug。当所有潜在的读者都执行完成后,调用 call_rcu 释放旧副本。同 Spin lock 一样,RCU 同步技术主要适用于 SMP 环境。
内核无锁第四层级 — 免锁
环形缓冲区是生产者和消费者模型中常用的数据结构。生产者将数据放入数组的尾端,而消费者从数组的另一端移走数据,当达到数组的尾部时,生产者绕回到数组的头部。
如果只有一个生产者和一个消费者,那么就可以做到免锁访问环形缓冲区(Ring Buffer)。写入索引只允许生产者访问并修改,只要写入者在更新索引之前将新的值保存到缓冲区中,则读者将始终看到一致的数据结构。同理,读取索引也只允许消费者访问并修改。
图 2. 环形缓冲区实现原理图
如图所示,当读者和写者指针相等时,表明缓冲区是空的,而只要写入指针在读取指针后面时,表明缓冲区已满。
清单 9. 2.6.10 环形缓冲区实现代码
/*
* __kfifo_put - puts some data into the FIFO, no locking version
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these functions.
*/
unsigned int __kfifo_put(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->size - fifo->in + fifo->out);
/* first put the data starting from fifo->in to buffer end */
l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(fifo->buffer, buffer + l, len - l);
fifo->in += len;
return len;
}
/*
* __kfifo_get - gets some data from the FIFO, no locking version
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these functions.
*/
unsigned int __kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->in - fifo->out);
/* first get the data from fifo->out until the end of the buffer */
l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + l, fifo->buffer, len - l);
fifo->out += len;
return len;
}
以上代码摘自 2.6.10 内核,通过代码的注释(斜体部分)可以看出,当只有一个消费者和一个生产者时,可以不用添加任何额外的锁,就能达到对共享数据的访问。
相关推荐
透过 Linux 内核看无锁编程
结合减短的linux内核代码,详细的描述了无锁编程的应用,做并行程序设计的应该了解一下。
1.9万预算游戏主机配置:AMD 9800X3D + RTX 5070 Ti 装机指南
游戏主机DIY装机的核心,是在有限预算下实现性能最优分配,需要同时考虑CPU缓存效率与显卡渲染能力。AMD 9800X3D 基于 3D V-Cache 技术,通过增大三级缓存降低内存访问延迟,在电竞网游和模拟类游戏中显著提升最低帧率;而 RTX 5070 Ti 作为 Blackwell 架构次旗舰,搭配 DLSS 4 多帧生成,可在 2K/4K 分辨率下保持高画质流畅输出。两者组合均衡覆盖了 CPU 绑定与 GPU 绑定的游戏场景,是 1.9 万预算内兼顾帧数与画质、并留有AM5平台升级空间的经典方案。围绕
项目管理面试常见问题及答案
1. 项目管理软件工具知识, 2. 编制项目计划的技术, 3. 人员管理技能 4. 沟通技能 5. 原理体系知识(标生命周期和项目管理)。 项目管理软件工具知识 问题1:工期和工作量之间的差异是什么? 答案1:工期是商业/日历上的天数,与人数和工作量无关。工作量是与日历天数无关的人的工作。例如: 一天的工作量对于一个一只花50%在时间在上面的人来说,他的工期就是两天。...
POSIX信号量——匿名信号量
POSIX 信号量有两种类型:匿名信号量和命名信号量,其本质是计数型信号量,计数型信号量参考:https://blog.youkuaiyun.com/Liangren_/article/details/108593071。 匿名信号量只存在于内存中,这就要求使用信号量的线程必须可以访问内存,因此匿名信号量可以应用于同一进程中线程间的通信,不同的进程间需要映射这段内存到自己的地址空间。 命名信号量可以通过名字访问,因此可以应用于进程间的通信。 POSIX 信号量被定义为 sem_t 类型,使用前应该定义 sem.
SMETANA 安装使用教程
关于SMETANA的安装和简单使用信息
如何在windows下使用cmake编译源码
MyProject/└── build/在MyProject目录下创建并编辑。创建build目录并进入。使用cmake命令生成构建文件。使用适当的命令编译项目。运行生成的可执行文件。
浅谈Linux内核无锁编程原理
如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步。同步可分为阻塞型同步(Blocking Synchronization)和非阻塞型同步( Non-blocking Synchronization)。阻塞型同步是指当一个线程到达临界区时,因另外一个线程已经持有访问该共享数据的锁,从而不能获取锁资源而阻塞,直到另外一个线程释放锁。常见的同步原语有 mutex、semaphore 等。
全国30米分辨率逐年植被覆盖度(FVC)数据集
植被覆盖度是描述地表植被的重要参数与生态环境的基本指标,在大气圈、土壤圈、水圈和生物圈中都占据着重要的地位,区域及全球范围的植被覆盖度估算对植被及相关领域的研究具有重要意义。分别对LANDSAT 5/7/8系列数据计算FVC,其中Landsat5 和Landsat 7NDVI计算利用 B4和B3波段,Landsat8 和Landsat9 NDVI计算利用 B5和B4波段。算法从当年所有观测数据中,以低云、低探测角度和最高NDVI值为标准,选择最佳的可用像元值,并进行换算。(3) FVC的计算。
多进程编程- POSIX无名信号量
无名信号量(也称为匿名信号量)是一个同步原语,通常用于线程之间的同步,而不是进程之间。与命名信号量(用于进程间同步)相比,无名信号量的生命周期通常受限于创建它的进程,并且它们不需要一个与系统相关的名字。
昇腾CANN实战:如何用DVPP+AIPP实现32路视频流毫秒级预处理(附性能对比)
本文深入探讨了昇腾CANN架构中DVPP和AIPP模块在32路视频流毫秒级预处理中的硬件加速技术。通过异构计算架构的优化,实现了从传统CPU方案的40.7毫秒到CANN方案3.4毫秒的显著性能提升,特别适用于智能安防领域的实时视频分析需求。文章详细解析了DVPP多通道并行设计和AIPP模型级预处理融合的技术细节,并提供了性能对比数据与实战调优经验。
Pareto 优化 (Pareto Optimization) 算法详解及案例分析
Pareto 最优解是指在多目标优化问题中,无法通过改进一个目标而不损害其他目标的解。具体来说,对于一个解x∗x^*x∗,如果不存在另一个解xxx使得所有目标函数fixf_i(x)fix都不劣于fix∗f_i(x^*)fix∗,且至少有一个目标函数fjxf_j(x)fjx优于fjx∗f_j(x^*)fjx∗,则x∗x^*x∗是 Pareto 最优解。
转载:透过 Linux 内核看无锁编程
转载:透过 Linux 内核看无锁编程 sunnyliu 2010-07-30 14:55 浏览(3097) 评论(13) 收藏 【本文系外部转贴】 a9dcIwDVxDIWRyiguIfg42e4s7hd5LhMQQQSTNAa+4JPx593 杨 小华, Linux 内核开发工程师, 自由撰稿人 2010 年 1 月 21
uniapp(全端兼容) - 最新详细实现接入腾讯视频播放器功能插件,uniapp全平台端(小程序/H5网页/App/nvu)调用引入并使用腾讯视频播放组件完整全流程(详细示例源码,一键复制开箱即用)
uniapp腾讯视频,uniapp微信小程序腾讯视频,uniapp小程序接入腾讯视频详细示例教程源码,uniapp怎么在微信小程序平台使用腾讯视频插件,在uni-app中使用腾讯视频插件播放视频,uniapp调用腾讯视频,uniapp引入腾讯视频插件,uniapp小程序中引入腾讯视频无法播放,uniapp微信小程序平台引入腾讯视频播放不了黑屏,在uniapp开发的微信小程序中使用腾讯视频,uniapp如何引入腾讯视频插件播放视频,uni-app小程序平台引入腾讯视频插件播放腾讯视频,uniapp 小程序接入
【转载】Linux内核编程与应用编程对比
【转载】Linux内核编程与应用编程对比 转载链接1:http://www.arrowapex.cn/archives/66.html 在此之前也不清楚linux内核编程跟用户应用程序编程之间有什么不同,正好这几天做了一点linux模块编程,遇到问题请教朋友并查一些资料,感觉对内核编程和用户应用程序编程的几点不同有了一点体会,就写了下来。 1.linux内核编程和用户...
【WRF数据介绍第一期】静态地理数据介绍及下载
WRF数据介绍第一期:静态地理数据介绍及下载
android 蓝牙通讯demo
android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。
【转载】深度:一文看懂Linux内核!Linux内核架构和工作原理详解
原文链接http://www.openpcba.com/web/contents/get?id=4638&tid=15 简介 作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接起源于该进程。virt/ -..
大中型超市收银系统(可直接运行)
光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开
Linux学习之内核模块编程
内核模块显示进程控制块信息 安装模块 内核模块文件知识
jre9下载 jdk9
(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】
Outlook Express 6 (OE6) for Win7/Win8/Win10
英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。
linux内核同步的方法,[转载]linux内核同步方法-锁的使用
前面介绍了同步的概念以及使用的场合。接下来要介绍的是我们为了实现内核的同步,linux里面所提供的一些方法,当然从另外一个角度我们也可以看出linux的系统实现相当的完备。开头大致的罗列一下我们系统里面的同步方法吧:1.原子操作2.位操作3.自选锁4.读写锁5.信号量6.完成变量7.Seq锁8.禁止抢占9.BKL大内核锁10.。。。。。上面我们列举了那么多,不知道你常用那些来完成你的代码。反正我常...
[转载] Linux内核学习书籍
转自 https://blog.youkuaiyun.com/qq_34870631/article/details/83013431 关于内核学习我建议不要上来就读内核而是先了解内核的构成和特性,然后通过思考发现疑问这时再去读内核源码。即先了解概貌在读局部细节。而且内核分成好多部分,不要只是按照顺序去读,应该针对某一部分比如内存管理或进程管理横向读几本书,这样可以理解很全面。 Linux Kernel 四库...
【Linux 内核源码如何学习?】【转载】
在计算机科学中是一个用来管理软件发出的数据I/O(输入与输出)要求的计算机程序,将这些要求转译为数据处理的指令并交由中央处理器(CPU)及计算机中其他电子组件进行处理,是现代操作系统中最基本的部分。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并由内核决定一个程序在什么时候对某部分硬件操作多长时间。linux内核代码涉及知识点包括汇编指令、c语言、硬件组成原理、操作系统、数据结构和算法、各种外设总线、驱动、网络协议栈。直接对硬件操作是非常复杂的。
(转载)使用kgdb调试linux内核及内核模块
使用kgdb调试linux内核及内核模块创建时间:2005-09-09文章属性:原创文章提交:xcspy (xcspy.com_at_gmail.com)作者:xcspy成员 ladybugE-mail:xcspy.com@gmail.com主页:www.xcspy.com1. 几种内核调试工具比较kdb:只能在汇编代码级进行调试; 优点是不需要两台机器进行调试。gdb:在调试模块时缺少一
LINUX 和 WINDOWS 内核的区别(转载哦)
LINUX 和 WINDOWS 内核的区别(又是转载,什么时候自己也能弄个原创呢,%>_[声明:欢迎转载,转载请注明出自CU ACCESSORY http://linux.chinaunix.net/bbs/thread-1153868-1-1.html]关于LINUX和WINDOWS的口水站已经很多了。本文企图从技术角度来比较下2个主流操作系统的异同。偏重于内核部分。一、动机:我最早是 WIND
Linux 内核和 Windows 内核有什么区别?[转载 重学操作系统]
转载 转自 拉钩教育 重学操作系统 林䭽 操作系统内核:Linux 内核和 Windows 内核有什么区别? 专栏 我在这个文章基础上自己整理了一下留着自己未来去学习. 正文 Windows 和 Linux 是当今两款最主流的服务器操作系统产品,都拥有广泛的用户和信徒。Windows 通过强大的商业运作,驱动了大量优秀人才加盟到它的开发团队中;Linux 通过社区产品的魅力吸引着世界上大量的顶级程序员为他贡献源代码、解答问题。两者在服务器市场上竞争激烈,不分伯仲,但也存在互相扶持的关系。 我觉得,两个操作
670



被折叠的 条评论
为什么被折叠?



