前言

终究是问题,在讲我们平时学习零散的知识用到一起。最近老师安排了一个任务,在实现这个任务的时候,发现除了一个bug。是关于atf中gic配置的。但是这个是在s3过程中配置。

之前也学习过这几个东西,但是都是分散的!!!

但是这次这个问题,让我把这几个点简单的联系了起来。

感谢前辈的优秀blog!!!

以下将这次用到的几个知识做个整理:

在学习wdg源码的时候,看到了一个变量的定义方式:DECLARE_PER_CPU

一、per-CPU变量

1-前言-为什么需要per-CPU变量

假设系统中有4个cpu, 同时有一个变量在各个CPU之间是共享的,每个cpu都有访问该变量的权限。

当cpu1在改变变量v的值的时候,cpu2也需要改变变量v的值。这时候就会导致变量v的值不正确。这时候机智的你就会说,在cpu1访问变量v的时候可以使用原子操作加锁,cpu2访问变量v的时候需要等待。可是机智的是否考虑过加锁对性能的影响,原子操作对cpu是极耗cpu的。

再考虑一种情况,现在高速的cpu都带有高速缓冲cache。它介于cpu和主存之间,主要作用是加快cpu的访问速度。因为主存的访问速度相比cpu读写比较慢,在之间引入cache之后,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。

比如cpu1对变量v操作子后,变量v的值就发生了变化。而cpu2, cpu3, cpu4的cache中的值还是以前的值,所以这时候就需要将cpu2, cpu3, cpu4的cache中的值变为无效的,当cpu2读取变量v的时候就需要从内存中读取v。所以当某一个cpu对共享数据v做操作后,比较对其余的cache做无效操作,这也是对性能有所损耗的。

所以,就引入了per-cpu变量。

2-什么是per-CPU变量

per-CPU变量是linux系统一个非常有趣的特性,它为系统中的每个处理器都分配了该变量的副本。这样做的好处是,在多处理器系统中,当处理器操作属于它的变量副本时,不需要考虑与其他处理器的竞争的问题,同时该副本还可以充分利用处理器本地的硬件缓冲cache来提供访问速度。

per-CPU按照存储变量的空间来源分为静态per-CPU变量和动态per-CPU变量,前者的存储空间是在代码编译时静态分配的,而后者的存储空间则是在代码的执行期间动态分配的。

3-静态per-CPU变量声明和定义

声明DECLARE_PER_CPU宏:

<include/linux/percpu-defs.h>
----------------------------------------------------------------
#define DECLARE_PER_CPU(type, name)                 \DECLARE_PER_CPU_SECTION(type, name, "")#define DECLARE_PER_CPU_SECTION(type, name, sec)            \extern __PCPU_ATTRS(sec) __typeof__(type) name#define __PCPU_ATTRS(sec)                       \__percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) \PER_CPU_ATTRIBUTES
<include/asm-generic/percpu.h>
-----------------------------------------------------
#ifndef PER_CPU_BASE_SECTION
#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data..percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif
#endif

对上的宏定义DECLARE_PER_CPU使用例子: DECLARE_PER_CPU(int, val)来详细说明。

DECLARE_PER_CPUT(int, val)-> DECLARE_PER_CPU_SECTION(int, val, "")-> extern __PCPU_ATTRS("") __typeof__(int) val-> extern __percpu __attribute__((section(".data..percpu"))) int val

从上面的分析可以看出,该宏在源代码中声明了__percpu int val变量,该变量放在一个名为”.data…percpu”的section中。

定义DEFINE_PER_CPU宏:

<include/linux/percpu-defs.h>
----------------------------------------------------------------
#define DEFINE_PER_CPU(type, name)                  \DEFINE_PER_CPU_SECTION(type, name, "")#define DEFINE_PER_CPU_SECTION(type, name, sec)             \__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES            \__typeof__(type) name#ifndef PER_CPU_DEF_ATTRIBUTES
#define PER_CPU_DEF_ATTRIBUTES
#endif

对以上DEFINE_PER_CPU依然使用DEFINE_PER_CPU(int, val)做举例说明:

DEFINE_PER_CPU(int, val)
-> DEFINE_PER_CPU_SECTION(int, val, "")->__PCPU_ATTRS("") __typeof__(int) val-> __percpu __attribute__((section(".data..percpu"))) int val

可以看到,相比与DECLARE_PER_CPU,DEFINE_PER_CPU只是去掉了变量前面的extern, 所以使用DEFINE_PER_CPU(int,val)将会在源码中定义一个变量:

__percpu __attribute__((section(".data..percpu"))) int val

以下是per-CPU所有的形式的声明和定义:

4-静态per-CPU变量的链接脚本

在上一节per-CPU变量的声明和定义中,可以看到最后的变量都是存在一个”.data…percpu”段中。

. = ALIGN((1 << 12));
.data..percpu : AT(ADDR(.data..percpu) - 0)
{__per_cpu_load = .;__per_cpu_start = .;*(.data..percpu..first) . = ALIGN((1 << 12));*(.data..percpu..page_aligned) . = ALIGN(64);     *(.data..percpu..read_mostly) . = ALIGN(64); *(.data..percpu) *(.data..percpu..shared_aligned) __per_cpu_end = .;
}

可见,内核在编译链接的时候会把所有静态定义的per-CPU变量统一放到”.data…percpu”section中。链接器生成__per_cpu_start和__per_cpu_end两个变量表示该section的起始和结束地址。

5-动态分配per-CPU变量

  • 分配函数
#define alloc_percpu(type)                      \(typeof(type) __percpu *)__alloc_percpu(sizeof(type),       \__alignof__(type))

根据类型type,分配per-CPU变量

  • 释放函数
void free_percpu(void __percpu *ptr)

释放ptr所指向的per-CPU变量。

6-使用静态per-CPU变量

因为per-CPU不能像一般的变量那样访问,必须使用内核提供的函数:

#define get_cpu_var(var)                        \
(*({                                    \preempt_disable();                      \this_cpu_ptr(&var);                     \
}))#define put_cpu_var(var)                        \
do {                                    \(void)&(var);                           \preempt_enable();                       \
} while (0)

因为在get_cpu_var函数中关掉了抢占,所以在put_cpu_var中需要开启抢占。显然get函数和put函数需要匹配使用。
机智的你可能会问,为什么还需要关闭抢占,因为对于per-CPU来说已经是单处理器了。但是机智的你没有想到的是,在cpu访问per-CPU的时候,突然系统发生了一次紧急抢占,这时候cpu还在处理per-CPU变量,一旦被抢占了cpu资源,可能当前进程会换出处理器。所以关闭抢走还是必要的。

如果需要访问其他处理器的副本,可以使用函数per_cpu(var, cpu)

#define per_cpu(var, cpu)   (*per_cpu_ptr(&(var), cpu))

使用动态per-CPU变量

#define get_cpu_ptr(var)                        \
({                                  \preempt_disable();                      \this_cpu_ptr(var);                      \
})#define put_cpu_ptr(var)                        \
do {                                    \(void)(var);                            \preempt_enable();                       \
} while (0)#define per_cpu_ptr(ptr, cpu)   ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); })

其实我主要的目的是学习s3-suspend的流程,其中s3的流程。

suspend流程有着明晰的划分:PM Core–>Device PM–>syscore–>machine

其中wdg就是通过syscore实现suspend与resume的。于是这里来看看syscore是什么?

二、kernel syscore

1-syscore的概览

struct syscore_ops里有四个成员,一个list,三个函数指针(suspend, resume and shutdown)。

模块如果需要向syscore注册,先定义一个struct syscore_ops型变量(可只给部分函数指针赋值),然后使用register_syscore_ops进行注册,反之也可用unregister_syscore_ops解除注册。

syscore_suspend/syscore_resume 在系统的suspend/resume 中被调用, syscore_shutdown 在系统关机或者重启时被调用,对模块的工程师来说,知道它们会被调用就行了,如果是负责suspend/resume/shutdown/reboot,需要了解它们什么时候会被调用。

个人感觉可把一些重要的、需要晚于device进行suspend或者shutdown的动作,放到syscore中;或者把需要早于device进行resume的动作,放到syscore中。

2-syscore源码篇

syscore作为低功耗流程的一部分,其涉及的文件主要有syscore_ops.h和syscore.c,这一级别的回调函数是在完全屏蔽中断的场景下进行的。

1、主要结构体struct syscore_ops

该结构体是syscore操作的对象类型。

struct syscore_ops {struct list_head node;//用于链表控制,注册和删除syscore对象时操作此链表完成int (*suspend)(void);//睡眠流程时回调函数void (*resume)(void);//唤醒流程时回调函数void (*shutdown)(void);//这一级别的回调函数主要用于系统级的重启、停止或者掉电时才会使用
};

2、主要接口:

extern void register_syscore_ops(struct syscore_ops *ops);//注册syscore回调函数
extern void unregister_syscore_ops(struct syscore_ops *ops);//取消注册的回调函数
#ifdef CONFIG_PM_SLEEP
extern int syscore_suspend(void);//回调注册的syscore对象的suspend回调函数
extern void syscore_resume(void);//回调注册的syscore对象的resume回调函数
#endif
extern void syscore_shutdown(void);//回调注册的syscore对象的shutdown回调函数

3、具体实现,syscore.c

1)全局变量

static LIST_HEAD(syscore_ops_list);//初始化控制链表头结点,统一管理注册的syscore对象
static DEFINE_MUTEX(syscore_ops_lock);//访问上述链表时,通过此互斥信号量来互斥访问

2)register_syscore_ops:

其他组件主要通过此接口来注册回调函数,我们可以看到,把注册的对象存放在了syscore_ops_list链表中。

/*** register_syscore_ops - Register a set of system core operations.* @ops: System core operations to register.*/
void register_syscore_ops(struct syscore_ops *ops)
{mutex_lock(&syscore_ops_lock);list_add_tail(&ops->node, &syscore_ops_list);mutex_unlock(&syscore_ops_lock);
}

3)unregister_syscore_ops:

与register_syscore_ops功能相反,取消注册,从控制链表中删除。

/*** unregister_syscore_ops - Unregister a set of system core operations.* @ops: System core operations to unregister.*/
void unregister_syscore_ops(struct syscore_ops *ops)
{mutex_lock(&syscore_ops_lock);list_del(&ops->node);mutex_unlock(&syscore_ops_lock);
}

4)syscore_suspend:

该接口回调所有注册对象的suspend接口,该接口在suspend.c的suspend_enter函数中被调用,执行cpu掉电前的最后阶段操作。

/*** syscore_suspend - Execute all the registered system core suspend callbacks.** This function is executed with one CPU on-line and disabled interrupts.*/
int syscore_suspend(void)
{struct syscore_ops *ops;int ret = 0;pr_debug("Checking wakeup interrupts\n");/* Return error code if there are any wakeup interrupts pending. */ret = check_wakeup_irqs();if (ret)return ret;WARN_ONCE(!irqs_disabled(),"Interrupts enabled before system core suspend.\n");//我们可以看到,如果此时中断没有屏蔽掉,会有警告产生list_for_each_entry_reverse(ops, &syscore_ops_list, node)//按照链表逆序执行各个注册对象的suspend回调函数if (ops->suspend) {if (initcall_debug)pr_info("PM: Calling %pF\n", ops->suspend);ret = ops->suspend();if (ret)goto err_out;WARN_ONCE(!irqs_disabled(),"Interrupts enabled after %pF\n", ops->suspend);}return 0;err_out:pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend);list_for_each_entry_continue(ops, &syscore_ops_list, node)//如果有失败的,则执行已经执行过suspend回调的对象的resume回调if (ops->resume)ops->resume();return ret;
}
EXPORT_SYMBOL_GPL(syscore_suspend);

5)syscore_resume:

执行注册对象的resume回调,在suspend.c的唤醒流程中被调用

/*** syscore_resume - Execute all the registered system core resume callbacks.** This function is executed with one CPU on-line and disabled interrupts.*/
void syscore_resume(void)
{struct syscore_ops *ops;WARN_ONCE(!irqs_disabled(),"Interrupts enabled before system core resume.\n");list_for_each_entry(ops, &syscore_ops_list, node)if (ops->resume) {if (initcall_debug)pr_info("PM: Calling %pF\n", ops->resume);ops->resume();WARN_ONCE(!irqs_disabled(),"Interrupts enabled after %pF\n", ops->resume);}
}

6)syscore_shutdown:

该接口正常睡眠流程中不涉及调用,主要在sys.c中调用,涉及系统重启、halt等流程。

/*** syscore_shutdown - Execute all the registered system core shutdown callbacks.*/
void syscore_shutdown(void)
{struct syscore_ops *ops;mutex_lock(&syscore_ops_lock);list_for_each_entry_reverse(ops, &syscore_ops_list, node)if (ops->shutdown) {if (initcall_debug)pr_info("PM: Calling %pF\n", ops->shutdown);ops->shutdown();}mutex_unlock(&syscore_ops_lock);
}

参考链接:

  • https://blog.csdn.net/longwang155069/article/details/52033243:per-CPU变量
  • https://blog.csdn.net/lixiaojie1012/article/details/45625699:syscore

关于S3学习所涉及到的知识(一):per-CPU变量kernel syscore相关推荐

  1. 关于S3学习所涉及到的知识(四):GIC_V3寄存器介绍

    GIC_V3寄存器介绍 从这里看到,GIC分为3个主要的部分,要完成其作用,而且给用户进行预先配置的方法,GIC就提供了一组寄存器,这些寄存器的设计就是围绕着这些主要的功能来实现的. 1.Distri ...

  2. 机器学习算法、深度学习算法涉及的数学知识

    微积分基础 导数的定义 左导数.右导数.可导函数 导数几何意义.物理意义 基本函数求导公式 四则运算法则 复合函数求导法则 神经网络激活函数的导函数求解 高阶导数 导数与函数单调性 极值定理 导数与函 ...

  3. 学习SLAM需要哪些预备知识?

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 编辑:3D视觉工坊 链接:https://www.zhihu.com/question/3518606 ...

  4. DataWhale第21期组队学习自然语言处理实践(知识图谱)task4— 用户输入->知识库的查询语句

    参考来源:https://github.com/datawhalechina/team-learning-nlp/blob/master/KnowledgeGraph_Basic/task04.md# ...

  5. 数字图像直方图处理涉及的数学知识介绍

    ☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 在数字图像直方图处理学习时,老猿发现相关内容涉及数学定积分.概率统计等相关的 ...

  6. 学习进销存管理系统知识

    学习进销存管理系统知识 电子商务的发展和市场竞争的加剧将企业推上了风口浪尖,中小企业除了积极迎接挑战之外,别无选择. 网络的兴起与电子商务的发展带来了时空界限的突破.贸易方式的变革和经济活动的革命, ...

  7. 学习一个新领域的知识的最佳方法和最快时间各是什么?

    Liu Cao ,「学习方法」是个伪命题 玉某人.冷峻.淡之 等人赞同 有个TED演讲简直是为这个问题量身订做的. The first 20 hours-How to learn Anything. ...

  8. 小白学习Flink系列--第一篇(知识图谱)

    小白学习Flink系列–第一篇(知识图谱) 如何学习Flink? ​ 对于一门计算机技术来说,如何快速学习上手呢?具体的逻辑是什么呢?我认为有以下几条 了解技术的应用场景 技术的基本概念,如何使用,以 ...

  9. 超级详细学习了解进程和病毒知识

    超级详细学习了解进程和病毒知识 作者:不详 来源于:黑客在线 发布时间:2007-4-3 10:26:48 超级详细了解进程和病毒知识 第一:进程是什么 进程为应用程序的运行实例,是应用程序的一次动态 ...

最新文章

  1. html li 右跟下有倒影,HTML5 canvas实现的下雨夜湖面星空倒影动画特效
  2. Linq to SQL Dynamic 动态查询
  3. sqlite的几个常用方法
  4. linux进程管理命令实验,实验2Linux进程管理.doc
  5. 基于深度学习的视频预测研究综述
  6. font-familly:' 阿里巴巴-普惠体 '【永久免费 】 - 下载与使用
  7. 计算机应用与软件修审,国中课室素养导向标准本位评量的设计与应用:以英语科阅读为例...
  8. [李景山php]每天TP5-20170125|thinkphp5-Process.php-7
  9. springMVC ---- 异步调用
  10. SQL Server 数据库原理与应用
  11. 游戏设计阻力探秘之扩展空间
  12. 通过ida dump Uinity3D的加密dll
  13. 大牛教你如何利用积分商城API接口对接积分商城平台
  14. php 星期几大写,php日期转大写
  15. 网易云音乐评论 破解JS加密参数
  16. 基于G6-Editor的流程图编辑器
  17. 《勋伯格和声学》读书笔记(八):转调
  18. 云原生究竟怎么落地?
  19. 为什么可积不一定可导_函数可积、原函数存在、变上限函数的关系解读(绝对原创)...
  20. 如何编写Junit测试代码

热门文章

  1. 介绍一位朋友,如何从生物工程转到计算机,再到后面自学Python
  2. linux 输入法_新闻速读 gt; 百度输入法 Linux 版本发布 | Linux 中国
  3. (转)席德梅尔的海盗如何洗劫城市
  4. 《脱颖而出——成功网店经营之道》一第1章 电商风云起
  5. mongodb 百万_1亿条记录的MongoDB数据库随机查询性能测试
  6. 海思高端处理器追上联发科,一线大厂如坐针毡
  7. 基于生产者-消费者模式的桌面搜索器
  8. 什么是DOM及DOM操作?
  9. python仿360界面_python实现360的字符显示界面 -电脑资料
  10. XCode各版本对应的Mac OS操作系统版本