透过 Linux 内核看无锁编程

STM32与小米CyberGear微电机的CAN通信协议深度解析 本文深度解析STM32与小米CyberGear微电机的CAN通信协议,涵盖硬件连接、CAN总线配置、通信协议解析及多电机协同控制。重点介绍了基于STM32的电机控制实践,包括运动模式、速度与位置模式设置,以及数据反馈与故障诊断,为机器人及自动化设备开发提供实用指南。 阅读详情

非阻塞型同步 (Non-blocking Synchronization) 简介

如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步。同步可分为阻塞型同步(Blocking Synchronization)和非阻塞型同步( Non-blocking Synchronization)。

阻塞型同步是指当一个线程到达临界区时,因另外一个线程已经持有访问该共享数据的锁,从而不能获取锁资源而阻塞,直到另外一个线程释放锁。常见的同步原语有 mutex、semaphore 等。如果同步方案采用不当,就会造成死锁(deadlock),活锁(livelock)和优先级反转(priority inversion),以及效率低下等现象。

为了降低风险程度和提高程序运行效率,业界提出了不采用锁的同步方案,依照这种设计思路设计的算法称为非阻塞型算法,其本质特征就是停止一个线程的执行不会阻碍系统中其他执行实体的运行。

当今比较流行的 Non-blocking Synchronization 实现方案有三种:

  1. Wait-free

    Wait-free 是指任意线程的任何操作都可以在有限步之内结束,而不用关心其它线程的执行速度。 Wait-free 是基于 per-thread 的,可以认为是 starvation-free 的。非常遗憾的是实际情况并非如此,采用 Wait-free 的程序并不能保证 starvation-free,同时内存消耗也随线程数量而线性增长。目前只有极少数的非阻塞算法实现了这一点。

  2. Lock-free

    Lock-Free 是指能够确保执行它的所有线程中至少有一个能够继续往下执行。由于每个线程不是 starvation-free 的,即有些线程可能会被任意地延迟,然而在每一步都至少有一个线程能够往下执行,因此系统作为一个整体是在持续执行的,可以认为是 system-wide 的。所有 Wait-free 的算法都是 Lock-Free 的。

  3. 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. 加锁层级
图 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            
 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 �,%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;ip[i] = NULL; 
  } 
  old = ids->entries; 
   
  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. 环形缓冲区实现原理图
图 2. 环形缓冲区实现原理图

如图所示,当读者和写者指针相等时,表明缓冲区是空的,而只要写入指针在读取指针后面时,表明缓冲区已满。

清单 9. 2.6.10 环形缓冲区实现代码
  
 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); 
   
  l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 
  memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l); 
   
  memcpy(fifo->buffer, buffer + l, len - l); 
  fifo->in += len; 
  return len; 
 } 

  
 unsigned int __kfifo_get(struct kfifo *fifo, 
     unsigned char *buffer, unsigned int len) 
 { 
  unsigned int l; 
  len = min(len, fifo->in - fifo->out); 
   
  l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); 
  memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l); 
   
  memcpy(buffer + l, fifo->buffer, len - l); 
  fifo->out += len; 
  return len; 
 }

以上代码摘自 2.6.10 内核,通过代码的注释(斜体部分)可以看出,当只有一个消费者和一个生产者时,可以不用添加任何额外的锁,就能达到对共享数据的访问。

【CASS精品教程】006:CASS7.1 道路设计参数文件打开无响应,提示roadpara解决办法,权威解决办法汇总 在用CASS7.1 软件进行断面法土方量计算,在生成断面图后,需要进行道路设计参数文件的设计,打开断面设计参数文件的时候,无响应,提示roadpara,如下所示: 道路设计参数文件 解决办法有三种 第一种方法:Win+R (1)64位系统:输入regsvr32 C:\Windows\SysWOW64\CellCtrl5.ocx,回车 (2)32位系统输入regsvr32 C:... 阅读详情

相关推荐

Codesys电子凸轮从入门到应用:手把手教你用MC_CamIn/MC_CamOut实现主从轴同步

本文详细介绍了如何在Codesys平台上使用MC_CamIn和MC_CamOut功能块实现电子凸轮的主从轴同步控制。从环境配置、轴使能到凸轮表创建与动态切换,提供了完整的程序编写指南和调试技巧,帮助工程师快速掌握工业自动化中的高精度运动控制技术。

weixin_26878995的博客 277

单读者单写者的无锁队列

一个读线程,一个写线程,共同操作一个队列,不用锁如何避免冲突? 通常用锁是因为读写线程共同操作一个count计数器,改成两个线程各操作一个计数器,每次读写之前比较两个计数器的大小。 初始: head=-1; rear=0; a=0; b=0; 读线程: while(a>=b);//轮询 head=(head+1)%max; val=data[head];...

weixin_30724853的博客 262

手把手教你用示波器测试DDR5 DRAM信号完整性(附眼图分析技巧)

本文详细介绍了如何使用示波器测试DDR5 DRAM信号完整性,包括眼图分析技巧。从DDR5的核心挑战、测试设备选型到实测工作流程,提供了全面的指导。特别强调了DFE功能验证和系统级协同测试,帮助工程师解决信号完整性问题,提升测试效率。

g2h3i4j5的博客 1155

【原创】 linux cvlc&nb…

sudo /usr/share/doc/libdvdread4/install-css.sh sudo apt-get install ubuntu-restricted-extras To install VLC in Ubuntu/Linux Mint open Terminal (Press Ctrl+Alt+T) and copy the following commands in t

张志鹏的博客 736

【原创教程】汇川H5U入门教案第二课

定时器指令IN输入能流从OFF变为ON时,定时器启动计时,输出Q为ON,IN输入能流从ON变为OFF时,在IN保持为OFF期间,定时器的运行时间是PT参数指定的时间,定时器定时时间达到PT参数指定的时间后,Q变为OFF。IN输入能流为ON时,ET输出值为0,IN从ON变为OFF时,在定时器计时运行期间,ET输出当前的计时时间,定时器计时时间到达PT参数指定的时间后,ET值保持。IN输入能流为ON时,在定时器计时运行期间,ET输出当前的计时时间,定时器计时时间到达PT参数指定的时间后,ET值保持;

Qinmingyue13的博客 2480

osgPython

osgPython是一组 Python 模块,为 OpenSceneGraph 的 C++ 库提供了完整的 Python 接口。这意味着你可以在 Python 中调用几乎所有的 OSG 功能,从创建简单的场景到复杂的渲染效果。核心优势开发速度快:Python 语法简洁,无需编译,快速迭代易于学习:比 C++ 更易上手,适合原型开发和教学生态丰富:可以结合 NumPy、Matplotlib 等科学计算库跨平台:与 OSG 一样支持 Windows、Linux、macOS性能考虑。

深之JohnChen的专栏 454

Linux查看服务命令与端口使用状态

netstat/lsof netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况 -a 显示一个所有的有效连接信息列表(包括已建立的连接,也包括监听连接请求的那些连接) -n 显示所有已建立的有效连接 -t tcp协议 -u udp协议 -l 查询正在监听的程序 -p 显示正在使用socket的...

iteye_2746的博客 668

7、Python高光谱分析工具(PyHAT)简介

Python高光谱分析工具(PyHAT)是一个开源、跨平台的Python库及图形用户界面,用于行星光谱数据的可视化与分析。它支持多种高光谱传感器数据,如火星勘测轨道飞行器的CRISM、月船1号的M3以及SELENE Kaguya SP点光谱仪,提供预处理、衍生产品生成、统计学习和机器学习分析功能。PyHAT基于Pandas和NumPy构建,兼容Jupyter和QGIS环境,旨在解决现有光谱分析方法缺乏开源实现、难以探索性分析和算法分散的问题。其模块化架构涵盖数据输入/输出、通用光谱变换、测绘与点观测工具及高

aa123的博客 138

【原创】打造 Linux(ubuntu) 下的L…

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 打造 Linux(ubuntu) 下的Latex集成环境。 --------------------------- 一、概述 采用tex

kayneo的博客 412

十一、增强现实

那都是n年前的老版本了,新版本完全没有这个问题,我就是纠结这个问题,搭建若干环境,又是vmware虚拟机,又是msys2,又是anaconda,又是找机器,折腾了几天,结果通通不是。以一张实际卡片为锚点,通过笔记本的摄像头,在显示器上将一个计算机内的3d物体显示在卡片上,类似于电影上看到的那种全息投影技术;如果你是想在windows环境下开发linux运行程序,那么CygWin/gcc是你的不二之选。而如果你想开发的是windows运行程序,并且追求速度,那么二者相比而言,MinGW是更好的选择。

3189

OSG第三方库编译之二十一:minizip编译(Windows、Linux、Macos环境下编译)

OSG编译;第三方库;跨平台编译;minizip编译(Windows、Linux、Macos环境下编译)

LuckyHanMo的博客 2621

多读者多写者的无锁队列

单读者单写者很容易进行冲突避免,毕竟他们操作的是不同的指针(head和rear),而多读者则需要共同操作head,多写者共同操作rear,似乎必须要加锁,锁机制是内核提供的一种对象,比较影响效率。 可以借鉴spinlock的实现机制,利用CPU提供的原子指令(x86上有cmpxchg,即比较交换,当某个内存地址里的值等于某个值时,则将该地址里的内容与某个寄存器交换,这一系列操作是原子的,由CPU...

weixin_30776545的博客 271

[RAM] DRAM 导论:DDR4 | DDR5 | LPDDR5 | GDRR6 | HBM 应运而生

有数据的地方就有存储, 内存是谁都离不开的产品。而动态随机存取存储器(DRAM)作为一种常见的内存。本博客将介绍DRAM的基本概念、工作原理、优缺点以及应用领域等方面。

串起点滴 3918

解析&nbsp;Linux&nbsp;内核可装载模块的版本…

作者:王华东, 系统工程师, 自由职业者 王华东,自由职业者,对 Linux、开放源码感兴趣。   简介: 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可装载模块这一特性。内核可装载模块就是可在内核运行时加载到内核的一组代码。通常 , 我们会在两个版本不同的内核上装载同一模块失败,即使是在两个相邻的补丁级(Patch Level)版本上。这是因为内核在引入可装载模块的

Snail239的专栏 721

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

深入分析&nbsp;Linux&nbsp;内核链表&nbsp;(转载)

(2009-09-03 22:08) 分类: Linux Kernel   本文详细分析了 2.6.x 内核中链表结构的实现,并通过实例对每个链表操作接口进行了详尽的讲解。 一、 链表数据结构简介 链 表是一种常用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现方式。相对于数组,链表具有更好的动态性, 建立链表时无需预先知道数据总量,可

shangyaowei的专栏 408

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Linux&nbsp;V3.10&nbsp;内核的变化…

把一个在3.8内核运行的内核模块源码放到3.10内核编译失败,编译显示在kernel 3.8 下调用的 create_proc_entry() 函数未定义。总结一下这个内核模块源码在3.10与3.8编译通过的区别。 1. create_proc_entry() 函数 1 static inline struct proc_dir_entry *create_proc_entr

无语僧 739

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

深入分析&nbsp;Linux&nbsp;内核链表

一、 链表数据结构简介 链表是一种常用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现方式。相对于数组,链 表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序 性和组织链的空间损失。 通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域

182

[转]inotify&nbsp;--&nbsp;Linux&nbsp;2.6&nbsp;内核中…

reprinted from:   http://www.ibm.com/developerworks/cn/linux/l-inotifynew/ 一、 引言 众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供

r_512的专栏 468

linux&nbsp;tftp设置&nbsp;+&nbsp;uboot下tftp下载…

linux tftp设置: 1,安装tftp服务端(在linux订制时可设置),如要在linux中使用tftp命令,还需下载tftp客户端,也就是没有server字符的rpm。默认目录/tftpboot。 2,修改tftp配置文件,/etv/xinted.d/tftp。 3,重启tftp,/etc/init.d/xinet.d restart。 4,查看tftp是否启动成功。netstat -a

溪孟羽的专栏 730

Linux&nbsp;启动脚本&nbsp;完全注释&nbsp;1&nbsp;--&nbsp;整…

原文地址:http://blog.youkuaiyun.com/soulxu/archive/2008/05/11/2432942.aspx Linux 启动脚本 完全注释 1 -- 整体流程       Linux太伟大了,什么东西都可以由你来做主,Linux加载完内核后,所有的其他系统服务,进程,都是靠脚本来加载的。所以Linux是这么的透明,你可以清除的指导你的系统做了什么。      下面我就对Li

tomora的专栏 728

inotify&nbsp;--&nbsp;Linux&nbsp;2.6&nbsp;内核中的文…

一、 引言 众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机

tomora的专栏 584

提高&nbsp;Linux&nbsp;上&nbsp;socket&nbsp;性能

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 使用 Sockets API,我们可以开发客户机和服务器应用程序,它们可以在本地网络上进行通信,也可以通过 Internet 在全球范

490

TQ2440&nbsp;Linux&nbsp;系统移植&…

TQ2440 Linux 系统移植 分类: 嵌入式底层开发2011-02-23 21:37 936人阅读 评论(0) 收藏 举报   一、u-boot基本常用命令 1. 基本环境设置 setenv serverip 192.168.1.115 setenv ipaddr  192.168.1.211 setenv gatewayip 192.168.1.1 setenv ethaddr 1E

少波波波波少 561

Linux&nbsp;glibc&nbsp;的&nbsp;ma…

最近使用ACE的Message_Block时发现,程序运行一段时间之后内存越吃越多,即便没有请求,内存也不会下降。 在使用 valgrind 排除内存泄漏之后,把怀疑的对象转到了Message_Block上。 用ACE的测试用例改了一个测试程序: #include "ace/Log_Msg.h" #include "ace/Message_Block.h" #include #include

gao1738的专栏 622

linux&nbsp;下&nbsp;进程和线程的…

1、进程与线程 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。 线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥

senlin1202的博客 331

关于单机最大tcp连接数&nbsp;及&nbsp;linux&nbsp;…

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <div id="content" class= "content mod-cs-content text-content clearfi

danssion的专栏 534
上一篇: [原创]Linux下undefined&nbsp;refe…
下一篇: GCC的符号可见性——解决多个…
wakeupwakeup
博客等级 码龄11年 9粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值