openVswitch(OVS)源代码之linux RCU锁机制分析

分类: linux内核  |  标签: 云计算,openVswitch,linux内核,RCU锁机制  |  作者: yuzhihui_no1 相关  |  发布日期 : 2014-10-19  |  热度 : 1044°
 
 

前言

本来想继续顺着数据包的处理流程分析upcall调用的,但是发现在分析upcall调用时必须先了解linux中内核和用户空间通信接口Netlink机制,所以就一直耽搁了对upcall的分析。如果对openVswitch有些了解的话,你会发现其实openVswitch是在linux系统上运行的,因为openVswitch中有很多的机制,模块等都是直接调用linux内核的。比如:现在要分析的RCU锁机制、upcall调用、以及一些结构体的定义都是直接从linux内核中获取的。所以如果你在查看源代码的一些结构(或者模块,机制性代码)时,发现在openVswitch中没有定义(我用的是Source Insight来查看和分析源码,可以很好的查看是否定义过),那么很可能就是openVswitch包含了linux头文件引用了linux内核的一些定义。

RCU是linux的新型锁机制(RCU是在linux 2.6内核版本中开始正式使用的),本来一直纠结要不要用篇blog来说下这个锁机制。因为在openVswitch中有很多的地方用到了RCU锁,我开始分析的时候都是用一种锁机制一笔带过(可以看下openVswitch(OVS)源代码分析之数据结构里面有很多地方都用到了RCU锁机制)。后来发现有很多地方还用到了该锁机制的链表插入和删除操作,而且后面分析的代码中也有RCU的出现,所以就稍微的说下这个锁机制的一些特性和操作。

RCU运行原理

我们先来回忆下读写锁(rwlock)运行机制,这样可以分析RCU的时候可以对照着分析。读写锁分为读锁(也称共享锁),写锁(也称排他锁,或者独占锁)。分情况来分析下读写锁:

第一、要操作的数据区被上了读锁;1、若请求是读数据时,上读锁,多个读锁不排斥(即,在访问数据的读者上线未达到时,可以对该数据区再上读锁);2、若请求是写数据,则不能马上上写锁,而是要等到数据区的所有锁(包括读锁和写锁)都释放掉后才能开始上写访问。

第二、要操作的数据区上了写锁;则不管是什么请求都必须等待数据区的写锁释放掉后才能上锁访问。

同理来分析下RCU锁机制: RCU是read copy udate的缩写,按照单词意思就知道这是一种针对数据的读、复制、修改的保护锁机制。锁机制原理:

第一、写数据的时候,不需要像读写锁那样等待所有锁的释放。而是会拷贝一份数据区的副本,然后在副本中修改,等待修改完后。用这个副本替换原来的数据区,替换的时候就要像读写锁中上写锁那样,等到原数据区上所有访问者都退出后,才进行数据的替换;根据这种特性可以推断出,用RCU锁可以有多个写者,拷贝了多份数据区数据,修改后各个写着陆续的替换掉原数据区内容。

第二、读数据的时候,不需要上任何锁,也几乎不需要什么等待(读写锁中如果数据区有写锁则要等待)就可以直接访问数据。为什么说几乎不需要等待呢?因为写数据中替换原数据时,只要修改个指针就可以,消耗的时间可以说几乎不算,所以说读数据不需要其他额外开销。

总结下RCU锁机制特性,允许多个读者和多个写者同时访问共享数据区的内容。而且这种锁对多读少写的数据来说是非常高效的,可以让CPU减少些额外的开销。如果写得操作多了的话,这种机制就没读写锁那么好了。因为RCU写数据开销还是很大的,要拷贝数据,然后还要修改,最后还要等待替换。其实这个机制就好比我们在一台共享服务器上放了个文件,有很多个人一起使用。如果你只是看看这个文件内容,那么直接在服务器上cat查看就可以。但如果你要修改该文件,那么你不能直接在服务器上修改,因为你这样操作会影响到将要看这个文件或者写这个文件的人。所以你只能先拷贝到自己本机上修改,当最后确认保证正确时,然后就替换掉服务器上的原数据。

RCU写者工作图示

下面看下RUC机制下修改数据(以链表为例)

根据上面的图会发现其实替换的时候只要修改下指针就可以,原数据区内容在被替换后,默认会被垃圾回收机制回收掉。

linux内核RCU机制API

了解了RCU的这些机制原理,下面来看下linux内核中常使用的一些和RCU锁有关的操作。注意,本blog并不会过多的去深究RCU最底层的实现机制,因为分享RCU工作机制的目的只是为了更好的了解openVswitch中使用到的那部分代码的理解,而不是为了分析linux内核源代码,不要本末倒置。如果遇到个知识点就拼命的深挖,那么你看一份源代码估计得几个月。

rcu_read_lock();看到这里有人可能会觉得和上面有矛盾,不是说好的读者不需要锁吗?其实这不是和上读写锁的那种上锁,这仅仅只是标识了临界区的开始位置。表明在临界区内不能阻塞和休眠,也不能让写者进行数据的替换(其实这功能远不止这些)。rcu _read_unlock()则是和上面rcu_read_lock()对应的,用来界定一个临界区(就是要用锁保护起来的数据区)。

synchronize_rcu();当该函数被一个CPU调用时(一般是有写者替换数据时调用),而其他的CPU都在RCU保护的临界区读数据,那么synchronize_rcu()将会保证阻塞写者,直到所有其它读数据的CPU都退出临界区时,才中止阻塞,让写着开始替换数据。该函数作用就是保证在替换数据前,所有读数据的CPU能够安全的退出临界区。同样,还有个call_rcu()函数功能也是类似的。如果call_rcu()被一个CPU调用,而其他的CPU都在RCU保护的临界区内读数据,相应的RCU回调的调用将被推迟到其他读临界区数据的CPU全部安全退出后才执行(可以看linux内核源文件的注释,在Rcupdate.h文件中rcu_read_look()函数前面的注释)。

rcu_dereference(); 获取在一个RCU保护的指针,指向RCU读端临界区。他的指针以后可能会被安全地解除引用。说到底就是一个RCU保护指针。

list_add_rcu();往RCU保护的数据结构中添加一个数据节点进去。这个和一般的往链表中增加一个节点操作是类似的,唯一不同的是多了这条代码:rcu_assign_pointer(prev->next, new); 代码大概含义:分配指向一个新初始化的结构指针,将由RCU读端临界区被解除引用,返回指定的值。(说实话我也不太懂这个注释是什么意思)大概的解释下:就是让插入点的前一个节点的next指向新增加的new节点,为什么要单独用一条这个语句来实现,而不是用 prev->next = new;直接实现呢?这是因为prev->next本来是指向其他值得,有可能有CPU通过prev->next去访问其他RCU保护的数据了,所以如果你要插入一个RCU保护的数据结构中必要要调用这个语句,它里面会帮你处理好一些细节(比如有其他CPU使用后面的数据,直接使用prev->next可能会使读数据的CPU断开,产生问题),并且让刚加入的新节点也受到RCU的保护。这类的插入有很多,比如从头部插入,从尾 部插入等,实现都差不多,这里不一一细讲。

list_for_each_entry_rcu();这是个遍历RCU链表的操作,和一般的链表遍历差不多。不同点就是必须要进入RCU保护的CPU(即:调用了rcu_read_lock()函数的CPU)才能调用这个操作,可以和其他CPU共同遍历这个RCU链表。以此相同的还有其他变相的遍历及哈希链表的遍历,不细讲。

如果在openVswitch源代码分析中发现了有关RCU的分析和这里的矛盾,可以以这里为准,当然我也会校对下。

linux RCU锁机制分析的更多相关文章

  1. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  2. Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  3. Linux kernel workqueue机制分析

    Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...

  4. InnoDB锁机制分析

    InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工作机制.通过 ...

  5. 【转】MSSQLServer数据库事务锁机制分析

    锁是网络数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性和一致性.各种大型数据库所采用的锁的基本理论是一致的,但在具体实现上各有差别.目前,大多数数据库管理系统都或多或少具有自我调 ...

  6. [转载] 数据库分析手记 —— InnoDB锁机制分析

    作者:倪煜 InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工 ...

  7. [转]Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    转载地址:https://www.cnblogs.com/MYSQLZOUQI/p/4233630.html 自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱 ...

  8. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  9. Linux内核NAPI机制分析

    转自:http://blog.chinaunix.net/uid-17150-id-2824051.html 简介:NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采用 ...

随机推荐

  1. idea 新写的jsp没有找到

  2. WinForm textbox 全选

    原地址:忘了 textBox1.KeyPress += anyTextBox_KeyPress; private void anyTextBox_KeyPress(object sender, Sys ...

  3. 吴裕雄 python神经网络 花朵图片识别(9)

    import osimport numpy as npimport matplotlib.pyplot as pltfrom PIL import Image, ImageChopsfrom skim ...

  4. rsync sersync搭建

    目标服务器 192.168.100.47 源服务器    192.168.100.46 目标服务器配置 [root@node01 nodejs]# cat /etc/rsyncd.conf #日志文件 ...

  5. 2019.2.4 nfs原理和安装实验

    NFS 访问一个本地文件还是NFS共享文件对于客户端而言都是透明的,当文件打开的瞬间,内核会作出一个决定,如果是本地文件内核会将本地NFS共享文件内核会将NFS共享文件的所有引用传递给——>NF ...

  6. 学习JS的心路历程-类型

    前言 之前学JS时候都是靠着谷狗一路跌跌撞撞的学过来,从来没有去翻过MDN的文件,导致留了许多技术债给自己. 最近有幸遇到一位前辈并开始从头学JS,前辈表示学程序不看文件是想作死自己?于是我的第一份功 ...

  7. 简单的实现微信获取openid

    微信公众平台获取openid在公众号的开发中有很多用途,前段时间为实现用户使用公众号在登录一次以后可以免密登陆而使用了openid.开发过程中遇到了一些问题,在这里向需要且还没有获取到openid的米 ...

  8. 03_java基础(六)之CRUD实现

    1.简单实现 package com.day01.station.dao; /** * Created by Administrator on 2018/2/1. */ import java.sql ...

  9. JDK8中JVM堆内存划分

    一:JVM中内存 JVM中内存通常划分为两个部分,分别为堆内存与栈内存,栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址. JVM全部的对象信息都 存放在堆内存中.相比 ...

  10. js保留小数点后面几位的方法

    原文地址: http://www.jb51.net/article/45884.htm 四舍五入以下处理结果会四舍五入: ? 1 2 var num =2.446242342; num = num.t ...