Linux内核入门到放弃-锁与进程间通信-《深入Linux内核架构》笔记

时间:2023-02-25 17:35:30

内核锁机制

对整数的原子操作

<asm-arch/atomic.h>
typedef struct {volatile int counter;} atomic_t;
//初始化只能借助于ATOMIC_INIT宏
atomic_t nmi_active = ATOMIC_INIT(0);

atomic_read(atomic_t *v);
atomic_set(atomic_t *v,int i);
........

自旋锁

自旋锁用于保护短的代码段,其中只包含少量C语句,因此会很快执行完毕。

自旋锁通过spinlock_t数据结构实现,基本上可使用spin_lock和spin_unlock操纵。还有其他一些自旋锁操作:spin_lock_irqsave(spin_unlock_irqsave)不仅获得自旋锁,还停用本地CPU的中断,而spin_lock_bh(spin_unlock_bh)则停用softIRQ。

用法:

spinlock_t lock = SPIN_LOCK_UNLOCKED;
....
spin_lock(&lock);
//临界区
spin_unlock(&lock);

在使用自旋锁时必须注意下面两点:

  • 如果获得锁之后不释放,系统将变得不可用。所有的处理器,迟早需要进入锁对应的临界区。它们会进入无限循环等待锁释放,但等不到。这产生了死锁,从名称来看,这是个应该避免的情况。
  • 自旋锁不应该长期持有,因为所有等待锁释放的处理器都处于不可用状态,无法用于其他工作。

信号量

<asm-arch/semaphore.h>
struct semaphore{
    atomic_t count;
    int sleepers;
    wait_queue_head_t wait;
};

除了down操作之外,还有两种其他的操作用于获取信号量(不同于自旋锁,在退出信号量保护的 临界区时,只有up函数可用)

  • down_interruptible工作方式与down相同,但如果无法获得信号量,则将进程置于TASK_INTERRUPTIBLE状态。因此,在进程睡眠时可以通过信号唤醒。②
  • down_trylock试图获取信号量。如果失败,则进程不会进入睡眠等待信号量,而是继续正常执行。如果获取了信号量,则该函数返回false值,否则返回true

RCU机制

read-copu-update

RCU的性能很好,不过对内存有一定的开销,但大多数情况下可以忽略。这是个好事情,但好事总伴随着一些不那么好的事情。下面是RCU对潜在使用者提出的一些约束。

  • 对共享资源的访问在大部分时间应该是只读的,写访问应该相对很少.
  • 在RCU保护的代码范围内,内核不能进入睡眠状态.
  • 受保护资源必须通过指针访问.

RCU的原理很简单:该机制记录了指向共享数据结构的指针的所有使用者.在该结构将要改变时,则首先创建一个副本,在副本中修改.在所有进行读访问的使用者结束对旧副本的读取之后,指针可以替换为新的,修改后副本的指针.请注意,这种机制允许读写并发进行!

//使用
rcu_read_lock();
p = rcu_dereference(ptr);

if(p != NULL){
    awesome_function(p);
}
rcu_read_unlock();

struct super_duper *new_ptr = kmalloc(...);
new_ptr->meaning = xyz;
new_ptr->of = 42;
new_ptr>life = 23;
//修改
rcu_assign_pointer(ptr,new_ptr);

内存与优化屏障

  • mb()、rmb()、wmb()将硬件内存屏障插入到代码流程中。rmb()是读访问内存屏障。它保证在屏障之后发出的任何读取操作执行之前,屏障之前发出的所有读取操作都已经完成。wmb适用于写访问,语义与rmb类似。读者应该能猜到,mb()合并了二者的语义。
  • barrier插入一个优化屏障。该指令告知编译器,保存在CPU寄存器中、在屏障之前有效的所有内存地址,在屏障之后都将失效。本质上,这意味着编译器在屏障之前发出的读写请求完 成之前,不会处理屏障之后的任何读写请求.
  • read_barrier_depends()是一种特殊形式的读访问屏障,它会考虑读操作之间的依赖性。如果屏障之后的读请求,依赖于屏障之前执行的读请求的数据,那么编译器和硬件都不能重排这些请求
  • smb_mb()、smp_rmb()、smp_wmb()相当于上述的硬件内存屏障,但只用于SMP系统。它们在单处理器系统上产生的是软件屏障。

优化屏障的一个特定应用是内核抢占机制.要注意,preempt_disable对抢占计数器加1因而停用了抢占,preempt_enable通过对抢占计数减1而再次启用抢占.这两个命令之间的代码,可免受抢占的影响.看一看下列代码:

preempt_disable();
function_which_must_node_be_preempted();
preempt_enable();

如果编译器决定将代码重新排序如下,那么就相当麻烦了:

function_which_must_node_be_preempted();
preempt_disable();
preempt_enable();

preempt_disable();
preempt_enable();
function_which_must_node_be_preempted();

为了防止发生上诉的两种情况,不可抢占的部分都变为可抢占.preempt_disable和preempt_enable都加入了内存屏障

<preempt.h>

#define premmpt_disable()\
do{\
    inc_preempt_count();\
    barrier();\
}while(0)

#define premmpt_enable()\
do{\
...
    barrier();\
    preempt_check_resched();\
}while(0)

读者/写者锁

读者/写者自旋锁定义为rwlock_t数据类型。必须根据读写访问,以不同的方法获取锁(read_lock,read_unlock,write_lock,write_unlock).

读/写信号量的用法类似。所用的数据结构是struct rw_semaphore,down_read和up_read用于获取对临界区的读访问。写访问借助于down_write和up_write进

大内核锁

这是内核锁遗迹之一,它可以锁定整个内核,确保没有处理器在核心态并行允许.该锁称为大内核锁(big kernel lock).

BKL的一个特定是,它的锁深度会进行计数.这意味着在内核已经锁定时,仍然可以调用lock_kernel.对应的解锁操作必须调用同样的次数,以解锁内核,使其他处理器能够进入.

尽管BKL在内核中仍然有1 000多处,但它已经是过时的概念,内核开发者废弃了对它的使用.

互斥量

p289

近似的per-CPU计数器

基本思想:计数器的准确值存储在内存中某处,准确值所在内存位置之后是一个数组,每个数组项对应于系统中的一个CPU.

如果一个处理器想要修改计数器的值(加上或减去某个值n),它不会直接修改计数器的值,因为这需要防止其他的CPU访问计数器(这是一个费时的操作)。相反,所需的修改将保存到与计数器相 关的数组中特定于当前CPU的数组项。

如果某个特定于CPU的数组元素修改后的绝对值超出某个阈值,则认为这种修改有问题,将随之 修改计数器的值.

只要计数器改变适度,这种方案中读操作得到的平均值会相当接近于计数器的准确值。

<percpu_counter.h>
struct percpu_counter {
    spinlock_t lock;
    long count;
    long *counters;
}; 

<percpu_counter.h>
#if NR_CPUS >= 16
#define FBC_BATCH  (NR_CPUS*2)
#else
#define FBC_BATCH  (NR_CPUS*4)
#endif
 

System V 进程间通信

System V UNIX的3种进程间通信(IPC)机制(信号量,消息队列,共享内存)反映了3种相去甚远的概念,不过三者却有一个共同点.它们都使用了全系统范围的资源,可以由几个进程同时共享.

在各个独立进程都能够访问SysV IPC对象之前,IPC对象必须在系统内唯一表示.为此,每种IPC结构在创建时分配了一个号码.凡知道这个魔数的各个程序,都能够访问对应的结构.如果独立的应用程序需要彼此通信,则通常需要将该魔数永久地编译到程序中.一种备选方案是动态的产生一个保证唯一的魔数(静态分配的号码无法保证唯一).

在访问IPC对象时,系统采用了基于文件访问权限的一个权限系统.

信号量

System V 的信号量接口决不直观,因为信号量的概率已经远超其实际定义了.信号量不再当作是用于支持原子执行预定义操作的简单类型变量.相反,一个System V信号量现在是指一整套信号量,可以允许几个操作同时进行.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SEMKEY 1234L /*标识符*/
#define PERMS 0666 /*访问权限*/

struct sembuf op_down[1] = {0,-1,0};
struct sembuf op_up[1] = {0,1,0};

int semid = -1;/*信号量ID*/
int res;/*信号量操作的结果*/

void init_sem(){
        /*测试信号量是否已经存在*/
        semid = semget(SEMKEY,0,IPC_CREAT|PERMS);
        if(semid < 0){
                printf("Create the semaphore\n");

                semid = semget(SEMKEY,1,IPC_CREAT|PERMS);
                if(semid < 0){
                        printf("Couldn't create semaphore!\n");
                        exit(-1);
                }
                res = semctl(semid,0,SETVAL,1);
        }
}

void down(){
        res = semop(semid,&op_down[0],1);
}

void up(){
        res = semop(semid,&op_up[0],1);
}

int main(void)
{
        init_sem();
        printf("Before critical code\n");
        down();
        printf("In critical code\n");
        sleep(10);
        up();
        return 0;
}

消息队列

https://blog.csdn.net/Linux_ever/article/details/50346929

共享内存

https://blog.csdn.net/Linux_ever/article/details/50372573

Linux内核入门到放弃-锁与进程间通信-《深入Linux内核架构》笔记的更多相关文章

  1. Linux内核入门到放弃-设备驱动程序-《深入Linux内核架构》笔记

    I/O体系结构 总线系统 PCI(Peripheral Component Interconnect) ISA(Industrial Standard Architecture) SBus IEEE1 ...

  2. Linux内核入门到放弃-内存管理-《深入Linux内核架构》笔记

    概述 内存管理的实现涵盖了许多领域: 内存中的物理内存页管理 分配大块内存的伙伴系统 分配较小内存块的slab.slub和slob分配器 分配非连续内存块的vmalloc机制 进程的地址空间 在IA- ...

  3. Linux内核入门到放弃-虚拟文件系统-《深入Linux内核架构》笔记

    VFS的任务并不简单.一方面,它用来提供了一种操作文件.目录及其他对象的统一方法.另一方面,它必须能够与各种方法给出的具体文件系统的实现达成妥协,这些实现在具体细节.总体设计方面都有一些不同之处. 文 ...

  4. Linux内核入门到放弃-进程虚拟内存-《深入Linux内核架构》笔记

    进程地址空间的布局 <mm_types.h> <mm_types.h> struct mm_struct { ... unsigned long (*get_unmapped_ ...

  5. Linux内核入门到放弃-时间管理-《深入Linux内核架构》笔记

    低分辨率定时器的实现 定时器激活与进程统计 IA-32将timer_interrupt注册为中断处理程序,而AMD64使用的是timer_event_interrupt.这两个函数都通过调用所谓的全局 ...

  6. Linux内核入门到放弃-Ext2数据结构-《深入Linux内核架构》笔记

    Ext2文件系统 物理结构 结构概观 块组是该文件系统的基本成分,容纳了文件系统的其他结构.每个文件系统都由大量块组组成,在硬盘上相继排布: ----------------------------- ...

  7. Linux从入门到放弃、零基础入门Linux&lpar;第四篇&rpar;:在虚拟机vmware中安装centos7&period;7

    如果是新手,建议安装带图形化界面的centos,这里以安装centos7.7的64位为例 一.下载系统镜像 镜像文件下载链接https://wiki.centos.org/Download 阿里云官网 ...

  8. Linux从入门到放弃、零基础入门Linux&lpar;第三篇&rpar;:在虚拟机vmware中安装linux(二)超详细手把手教你安装centos6分步图解

    一.继续在vmware中安装centos6.9 本次安装是进行最小化安装,即没有图形化界面的安装,如果是新手,建议安装带图形化界面的centos, 具体参考Linux从入门到放弃.零基础入门Linux ...

  9. Linux内核入门到放弃-无持久存储的文件系统-《深入Linux内核架构》笔记

    proc文件系统 proc文件系统是一种虚拟的文件系统,其信息不能从块设备读取.只有在读取文件内容时,才动态生成相应的信息. /proc的内容 内存管理 系统进程的特征数据 文件系统 设备驱动程序 系 ...

随机推荐

  1. php简单框架的应用实例

    <html> <frameset rows="50%,50%"> <frame src="/Test/header.php"&gt ...

  2. JS:callee属性

    函数内部属性:在函数内部,有两个特殊的对象:arguments和this. arguments有一个callee属性,该属性是一个指针,指向拥有这个arguments对象的函数. function f ...

  3. AspnetIdentitySample

    https://github.com/rustd/AspnetIdentitySample http://www.asp.net/web-forms/overview/getting-started/ ...

  4. 《oracle每日一练》免安装Oracle客户端使用PL&sol;SQL

    免安装Oracle客户端使用PL/SQL Oracle客户端挺招人烦的,部署连接它的应用通常需要先安装它的客户端,安装程序要求在目标机器上写注册表,假设你没有洁癖的话,你仍可能被下面的事情绊住:当你的 ...

  5. C&num;检查标准图幅编号

    /// <summary> /// 检查是否为标准图幅编号 /// </summary> /// <param name="MapNumber"&gt ...

  6. 如何入侵Linux操作系统

    我发现了一个网站,于是常规入侵.很好,它的FINGER开着,于是我编了一个SHELL,aaa帐号试到zzz(by the way,这是我发现的一个网上规律,那就是帐号的长度与口令的强度成正比, 如果一 ...

  7. status状态栏实现字符串走动

    <script type="text/javascript" language="javascript"> var i = 0; var str=& ...

  8. java 读取文件的路径

    1. 通用定位到用户目录下:   String userDir = System.getProperty("user.dir"); 2. web项目定位到WEB-INF/class ...

  9. 错误Matplotlib is building the font cache using fc-list&period; This may take a moment&period;

    这上面的错误是因为你环境中没有安装GUI工具,但是你在代码中又想要显示图片,即有下面的语句: plt.imshow(np.transpose(npimg, (, , ))) plt.show() 那么 ...

  10. jquery简易tab切换

    切换tab 使用eq()函数 eq() 方法将匹配元素集缩减值指定 index 上的一个. //为项目 3 设置红色背景 <ul> <li>list item 1</li ...