《深入浅出DPDK》读书笔记(五):同步互斥机制

本文内容为读书笔记,摘自《深入浅出DPDK》


56.原子操作

原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。对原子操作的简单描述就是:多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。原子操作是其他内核同步方法的基石。


57.在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是“原子操作”。


58.CMPXCHG这条指令,它的语义是比较并交换操作数(CAS, Compare And Set)。而用XCHG类的指令做内存操作,处理器会自动地遵循LOCK的语义,可见该指令是一条原子的CAS单指令操作。


59.DPDK原子操作实现和应用

在理解原子操作在DPDK的实现之前,建议读者仔细阅读并且能够理解第2章的内容,那部分是我们理解内存操作的基础,因为原子操作的最终反映也是对内存资源的操作。

原子操作在DPDK代码中的定义都在rte_atomic.h文件中,主要包含两部分:内存屏蔽和原16、32和64位的原子操作API。

1. 内存屏障API

  • rte_mb():内存屏障读写API
  • rte_wmb():内存屏障写API
  • rte_rmb():内存屏障读API

这三个API的实现在DPDK代码中没有什么区别,都是直接调用__sync_synchronize(),而__sync_synchronize()函数对应着MFENCE这个序列化加载与存储操作汇编指令。

我们在这里给出一个内存屏障的应用在DPDK中的实例,在virtio_dev_rx()函数中,在读取avail->flags之前,加入内存屏障API以防止乱序的执行。

*(volatile uint16_t *)&vq->used->idx += count; 
vq->last_used_idx = res_end_idx; 
/* flush used->idx update before we read avail->flags. */ 
rte_mb(); 
/* Kick the guest if necessary. */ 
if (! (vq->avail->flags & VRING_AVAIL_F_NO_INTERRUPT)) 
      eventfd_write(vq->callfd, (eventfd_t)1); 

2. 原子操作API

DPDK代码中提供了16、32和64位原子操作的API,以rte_atomic64_add() API源代码为例,讲解一下DPDK中原子操作的实现,其代码如下:

static inline void 
rte_atomic64_add(rte_atomic64_t *v, int64_t inc) 
{ 
    int success = 0;3 
    uint64_t tmp; 
    while (success == 0) { 
        tmp = v->cnt; 
        success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, 
                                            tmp, tmp + inc); 
    } 
} 

我们可以看到这个API中主要是使用了比较和交换的原子操作API,关于比较和交换指令的原理我们已经在前面解释了,这里我们只是来看DPDK是如何嵌入汇编指令来使用它的。

rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src) 
{ 
    uint8_t res; 
    asm volatile( 
            MPLOCKED 
            "cmpxchgq %[src], %[dst]; " 
            "sete %[res]; " 
            :[res] "=a" (res), 
              [dst] "=m" (*dst) 
            :[src] "r" (src), 
              "a" (exp), 
              "m" (*dst) 
            :"memory"); 
    return res; 
} 

在VXLAN例子代码中,使用了64位的原子操作API来进行校验码和错误包的统计;这样,在多核系统中,加上原子操作的数据包统计才准确无误。

int 
vxlan_rx_pkts(struct virtio_net *dev, struct rte_mbuf **pkts_burst, 
      uint32_t rx_count) 
{ 
  uint32_t i = 0; 
  uint32_t count = 0; 
  int ret; 
  struct rte_mbuf *pkts_valid[rx_count]; 
  for (i = 0; i < rx_count; i++) { 
      if (enable_stats) { 
          rte_atomic64_add( 
              &dev_statistics[dev->device_fh].rx_bad_ip_csum, 
              (pkts_burst[i]->ol_flags & PKT_RX_IP_CKSUM_BAD) 
              != 0); 
          rte_atomic64_add( 
              &dev_statistics[dev->device_fh].rx_bad_ip_csum, 
              (pkts_burst[i]->ol_flags & PKT_RX_L4_CKSUM_BAD) 
              != 0); 
      } 
      ret = vxlan_rx_process(pkts_burst[i]); 
      if (unlikely(ret < 0)) 
          continue; 
      pkts_valid[count] = pkts_burst[i]; 
          count++; 
  } 
  ret = rte_vhost_enqueue_burst(dev, VIRTIO_RXQ, pkts_valid, count); 
  return ret; 
} 

60.DPDK读写锁实现和应用

DPDK读写锁的定义在rte_rwlock.h文件中,

  • rte_rwlock_init(rte_rwlock_t *rwl):初始化读写锁到unlocked状态。
  • rte_rwlock_read_lock(rte_rwlock_t *rwl):尝试获取读锁直到锁被占用。
  • rte_rwlock_read_unlock(rte_rwlock_t *rwl):释放读锁。
  • rte_rwlock_write_lock(rte_rwlock_t *rwl):获取写锁。
  • rte_rwlock_write_unlock(rte_rwlock_t *rwl):释放写锁。

读写锁在DPDK中主要应用在下面几个地方,对操作的对象进行保护。

  • ❑在查找空闲的memory segment的时候,使用读写锁来保护memseg结构。LPM表创建、查找和释放。
  • ❑Memory ring的创建、查找和释放。
  • ❑ACL表的创建、查找和释放。
  • ❑Memzone的创建、查找和释放等。

下面是查找空闲的memory segment的时候,使用读写锁来保护memseg结构的代码实例。

/* 
  * Lookup for the memzone identified by the given name 
  */ 
const struct rte_memzone * 
rte_memzone_lookup(const char *name) 
{ 
        struct rte_mem_config *mcfg; 
        const struct rte_memzone *memzone = NULL; 
        mcfg = rte_eal_get_configuration()->mem_config; 
        rte_rwlock_read_lock(&mcfg->mlock); 
        memzone = memzone_lookup_thread_unsafe(name); 
        rte_rwlock_read_unlock(&mcfg->mlock); 
        return memzone; 
} 

61.自旋锁的缺点

自旋锁必须基于CPU的数据总线锁定,它通过读取一个内存单元(spinlock_t)来判断这个自旋锁是否已经被别的CPU锁住。

  • 1)自旋锁一直占用CPU,它在未获得锁的情况下,一直运行——自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。
  • 2)在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数(如copy_to_user()、copy_from_user()、kmalloc()等)也可能造成死锁。

自旋锁使用时有两点需要注意:

  • 1)自旋锁是不可递归的,递归地请求同一个自旋锁会造成死锁。
  • 2)线程获取自旋锁之前,要禁止当前处理器上的中断。(防止获取锁的线程和中断形成竞争条件)

62.DPDK自旋锁实现和应用

DPDK中自旋锁API的定义在rte_spinlock.h文件中,其中下面三个API被广泛的应用在告警、日志、中断机制、内存共享和link bonding的代码中,用于临界资源的保护。

rte_spinlock_init(rte_spinlock_t *sl); 
rte_spinlock_lock(rte_spinlock_t *sl); 
rte_spinlock_unlock (rte_spinlock_t *sl); 

其中rte_spinlock_t定义如下,简洁并且简单。

/** 
  * The rte_spinlock_t type. 
  */ 
typedef struct { 
    volatile int locked; /**< lock status 0 = unlocked, 1 = locked */ 
} rte_spinlock_t; 

下面的代码是DPDK中的vm_power_manager应用程序中的set_channel_status_all()函数,在自旋锁临界区更新了channel的状态和变化的channel的数量,这种保护在像DPDK这种支持多核的应用中是非常必要的。

int 
set_channel_status_all(const char *vm_name, enum channel_status status) 
{ 
    … 
    rte_spinlock_lock(&(vm_info->config_spinlock)); 
    mask = vm_info->channel_mask; 
    ITERATIVE_BITMASK_CHECK_64(mask, i) { 
        vm_info->channels[i]->status = status; 
        num_channels_changed++; 
    } 
    rte_spinlock_unlock(&(vm_info->config_spinlock)); 
    return num_channels_changed; 
} 

63.无锁机制

Linux内核无锁环形缓冲

环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的数据。

在Linux内核代码中,kfifo就是采用无锁环形缓冲的实现,kfifo是一种“First In First Out”数据结构,它采用了前面提到的环形缓冲区来实现,提供一个无边界的字节流服务。


64.DPDK无锁环形缓冲

DPDK ring库:环形缓冲区的解剖

基于无锁环形缓冲的的原理,Intel DPDK提供了一套无锁环形缓冲区队列管理代码,支持单生产者产品入列,单消费者产品出列;多名生产者产品入列,多名消费者出列操作。


系列文章

《深入浅出DPDK》读书笔记(一):基础部分知识点

《深入浅出DPDK》读书笔记(二):网卡的读写数据操作

《深入浅出DPDK》读书笔记(三):NUMA - Non Uniform Memory Architecture 非统一内存架构

《深入浅出DPDK》读书笔记(四):并行计算-SIMD是Single-Instruction Multiple-Data(单指令多数据)

 

### 回答1: DPDK(Data Plane Development Kit)是一种高性能数据面开发工具包。深入浅出DPDK是一份详细介绍DPDK的PDF文档,适合初学者了解DPDK的基本概念和使用方法。 该文档从DPDK的介绍开始,介绍了DPDK的发展历史、使用场景和目标。接着详细介绍了DPDK的架构,包括主要组件和数据流向。其中详细介绍了包管理的基本原理和流程、缓存池的管理方式、内存对齐和地址映射等内容。 文档还介绍了如何使用DPDK,包括DPDK的编译和安装、如何使用DPDK进行虚拟网络功能(VNF)处理,以及如何使用DPDK进行数据包嗅探和过滤等。 此外,该文档还介绍了DPDK的性能优化技巧,包括如何优化包接收和处理、如何使用多核心优化性能和如何配置DPDK与硬件交互。 总体而言,深入浅出DPDK是一份非常有价值的文档,它不仅为初学者提供了详细的DPDK介绍和使用指南,还涵盖了DPDK的高级主题和性能优化技巧,为使用DPDK进行高性能数据面开发的人员提供了重要的参考资料。 ### 回答2: DPDK(Data Plane Development Kit)是一个高性能数据平面开发工具包,它使用用户态技术实现了零拷贝的数据处理和高速的数据包转发。作为一款开源工具,DPDK已经被广泛应用于虚拟化、云计算及网络功能虚拟化等领域。 《深入浅出dpdk》是一份由DPDK社区编写的指南,它的目的是帮助开发人员更好地了解DPDK,并使用DPDK构建高性能网络应用程序。该指南提供了详细的DPDK架构、API接口、应用案例和性能调优等方面的介绍,同时也介绍了其他相关技术,例如硬件加速、 NUMA架构、 数据库加速、分布式系统等。 《深入浅出dpdk》PDF版本可以从DPDK社区网站上自由下载,它包含了大量易懂的图表和代码实例,让开发人员更容易理解DPDK的概念和原理。对于想要在高性能数据平面开发方面取得突破的开发人员来说,这份指南是不可或缺的学习资料。 需要注意的是,《深入浅出dpdk》不仅是一份教材,更是一份指导开发人员如何使用DPDK进行数据平面开发的实际指南。开发人员在阅读本指南的同时,也需要具备一定的网络编程基础和C语言编程能力。同时,开发过程中还需要注意DPDK版本的兼容性和网络设备的支持情况。 ### 回答3: DPDK(Data Plane Development Kit)是一种高性能数据面开发工具包,它可以帮助开发人员快速地实现高性能网络应用程序。本篇文章解析的Deep Dive into DPDK的PDF文件是一份帮助初学者深入了解DPDK的指南,其中详细地介绍了DPDK的各种重要概念、特性和使用方法。 首先,这份PDF文件详细介绍了DPDK的架构和工作原理。DPDK采用轮询机制减少了对内核的依赖,从而显著提高了网络数据处理的性能。此外,本文还介绍了DPDK中各种组件的作用和功能,例如EAL(Environment Abstraction Layer)、Mempool、Ring等。这些组件都是DPDK中重要的工具,它们可以帮助开发人员更好地构建高性能应用程序。 其次,本文还介绍了如何使用DPDK来构建网络应用程序。首先,开发人员需要安装DPDK,并正确配置相应环境变量。其次,本文还介绍了如何使用DPDK的API来实现数据包的发送和接收。这些API提供了高效的数据传输方式和内存管理方式,开发人员可以根据自己的需求进行自定义。 此外,本文还介绍了DPDK的一些高级功能,例如NUMA支持、内存优化等。这些功能可以帮助开发人员更好地控制和管理系统资源,从而确保系统的高性能和稳定性。 总之,本文是一份非常实用的DPDK指南,其中介绍了DPDK的各种重要概念、特性和使用方法。初学者可以通过阅读这份文件,快速掌握DPDK的核心知识,为构建高性能应用程序打下牢固的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值