首页
chatGPT
关于
友链
其它
统计
更多
壁纸
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,400 阅读
2
修改Linux Kernel defconfig的标准方法
6,379 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
5,963 阅读
4
使用git生成patch和应用patch
3,450 阅读
5
c语言的__attribute__
3,168 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
网址
adtxl
累计撰写
367
篇文章
累计收到
14
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
页面
chatGPT
关于
友链
其它
统计
壁纸
留言
搜索到
367
篇与
的结果
2020-10-16
Android Service Framework、Binder Driver相关术语
相关术语:服务管理器(Service Server):指运行系统服务的进程,如System Server, Media Server服务客户端(Server Client):指使用系统服务的进程上下文管理器(Context Manager):是一个管理系统服务的系统进程,它管理安装在系统中的各种系统服务的位置信息Handle, 这些Handle用来指定Binder IPC的目的地址。服务框架(Service Framework):包含前面提到的Service Manager,其中定义了一系列类,用于服务使用者与系统服务间的RPC操作。服务接口(Service Interface):它是一个预先定义的接口,用在服务使用者与系统服务间。系统服务应该根据相关接口实现Stub函数,并提供相关服务。而服务使用者也必须根据相关接口调用服务。服务使用者:在服务客户进程中实际使用服务的模块。服务(Service):由服务Stub函数实现定义在服务接口中的功能,是提供实际服务功能的模块。服务代理(Service Proxy):执行RPC时用来对数据进行Marshalling处理的对象,不同的服务接口对应不同的服务代理。它提供服务代码函数,根据服务接口中定义的函数,对数据分别进行不同的Marshalling处理。服务Stub:RPC执行时用来对数据进行UnMarshalling处理的对象,该对象随接口不同而不同。它对接收到的数据进行UnMarshalling处理后,调用相关的服务Stub函数Binder Driver:Binder是Android中为支持IPC而采用的机制,它以Android Linux内核的Device Driver形态存在。Binder IPC:它是Android中进程间通过Binder Driver交换数据的方式。Binder IPC数据:一种用在Service Framework与Binder Driver间的数据格式。Binder RPC:服务会向使用者提供基于特定服务接口的函数,服务使用者通过Binder IPC调用这些函数,就像调用自身函数一样。Binder IPC内部是基于Binder IPC机制的。Binder RPC数据:服务使用者与服务间进行Binder IPC时的数据。
2020年10月16日
844 阅读
0 评论
0 点赞
2020-10-15
thermal代码分析(2)thermal_core.c
thermal\_core是thermal框架的核心,负责将thermal\_zone、thermal\_cooling和thermal\_governor连接起来首先看下头文件thermal_core.h,/* Initial state of a cooling device during binding */ #define THERMAL_NO_TARGET -1UL /* * This structure is used to describe the behavior of * a certain cooling device on a certain trip point * in a certain thermal zone */ // thermal_instance结构体定义 struct thermal_instance{ ...... }; // 注册和卸载governor int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); // 下面通过宏判断使用哪个governor #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); void thermal_gov_step_wise_unregister(void); #else static inline int thermal_gov_step_wise_register(void) { return 0; } static inline void thermal_gov_step_wise_unregister(void) {} #endif /* CONFIG_THERMAL_GOV_STEP_WISE */ ... ... /* device tree support */ #ifdef CONFIG_THERMAL_OF int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } static inline void of_thermal_destroy_zones(void) { } static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) { return 0; } static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) { return false; } static inline const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *tz) { return NULL; } #endif #endif /* __THERMAL_CORE_H__ */在thermal_core.c除了以上函数的具体实现,还包括一些其它函数,首先看一下thermal的初始化函数thermal\_init()函数:static int __init thermal_init(void) { int result; result = thermal_register_governors(); // 注册governor if (result) goto error; result = class_register(&thermal_class); // 注册/sys/class/thermal if (result) goto unregister_governors; result = genetlink_init(); // generic netlink初始化,干嘛的? if (result) goto unregister_class; result = of_parse_thermal_zones(); // 解析dts文件中的“thermal-zone”节点,并注册thermal_zone_device if (result) goto exit_netlink; result = register_pm_notifier(&thermal_pm_nb); // 注册notifier if (result) pr_warn("Thermal: Can not register suspend notifier, return %d\n", result); return 0; exit_netlink: genetlink_exit(); unregister_class: class_unregister(&thermal_class); unregister_governors: thermal_unregister_governors(); error: idr_destroy(&thermal_tz_idr); idr_destroy(&thermal_cdev_idr); mutex_destroy(&thermal_idr_lock); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); return result; }接下来,看一下of_parse_thermal_zones函数,我们从设备树文件中找个thermal-zones节点,如下所示thermal-zones { soc_thermal { polling-delay = <1000>; polling-delay-passive = <100>; sustainable-power = <2150>; thermal-sensors = <&aml_sensor0 3>; trips { switch_on: trip-point@0 { temperature = <70000>; hysteresis = <1000>; type = "passive"; }; control: trip-point@1 { temperature = <80000>; hysteresis = <1000>; type = "passive"; }; hot: trip-point@2 { temperature = <85000>; hysteresis = <5000>; type = "hot"; }; critical: trip-point@3 { temperature = <260000>; hysteresis = <1000>; type = "critical"; }; }; cooling-maps { cpufreq_cooling_map { trip = <&control>; cooling-device = <&cpus 0 4>; contribution = <1024>; }; cpucore_cooling_map { trip = <&control>; cooling-device = <&cpu_cluster0 0 3>; contribution = <1024>; }; gpufreq_cooling_map { trip = <&control>; cooling-device = <&gpu 0 4>; contribution = <1024>; }; gpucore_cooling_map { trip = <&control>; cooling-device = <&gpucore 0 2>; contribution = <1024>; }; }; }; }; }; /* end of / */ 再看一下of\_parse\_thermal\_zones函数的具体实现,/** * of_parse_thermal_zones - parse device tree thermal data * * Initialization function that can be called by machine initialization * code to parse thermal data and populate the thermal framework * with hardware thermal zones info. This function only parses thermal zones. * Cooling devices and sensor devices nodes are supposed to be parsed * by their respective drivers. * * Return: 0 on success, proper error code otherwise * */ int __init of_parse_thermal_zones(void) { struct device_node *np, *child; struct __thermal_zone *tz; struct thermal_zone_device_ops *ops; np = of_find_node_by_name(NULL, "thermal-zones"); if (!np) { pr_debug("unable to find thermal zones\n"); return 0; /* Run successfully on systems without thermal DT */ } for_each_available_child_of_node(np, child) { struct thermal_zone_device *zone; struct thermal_zone_params *tzp; #ifdef CONFIG_AMLOGIC_TEMP_SENSOR const char *str; #endif int i, mask = 0; u32 prop; tz = thermal_of_build_thermal_zone(child); if (IS_ERR(tz)) { pr_err("failed to build thermal zone %s: %ld\n", child->name, PTR_ERR(tz)); continue; } ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL); if (!ops) goto exit_free; tzp = kzalloc(sizeof(*tzp), GFP_KERNEL); if (!tzp) { kfree(ops); goto exit_free; } /* No hwmon because there might be hwmon drivers registering */ tzp->no_hwmon = true; if (!of_property_read_u32(child, "sustainable-power", &prop)) tzp->sustainable_power = prop; #ifdef CONFIG_AMLOGIC_TEMP_SENSOR if (!of_property_read_string(child, "policy", &str)) strncpy(tzp->governor_name, str, THERMAL_NAME_LENGTH); #endif for (i = 0; i < tz->ntrips; i++) mask |= 1 << i; /* these two are left for temperature drivers to use */ tzp->slope = tz->slope; tzp->offset = tz->offset; // 注册thermal_zone_device zone = thermal_zone_device_register(child->name, tz->ntrips, mask, tz, ops, tzp, tz->passive_delay, tz->polling_delay); if (IS_ERR(zone)) { pr_err("Failed to build %s zone %ld\n", child->name, PTR_ERR(zone)); kfree(tzp); kfree(ops); of_thermal_free_zone(tz); /* attempting to build remaining zones still */ } } of_node_put(np); return 0; exit_free: of_node_put(child); of_node_put(np); of_thermal_free_zone(tz); /* no memory available, so free what we have built */ of_thermal_destroy_zones(); return -ENOMEM; } 在解析设备树函数中,将会调用thermal\_zone\_device设备注册函数,
2020年10月15日
1,107 阅读
0 评论
0 点赞
2020-10-14
thermal代码分析(1)Makefile文件
# # Makefile for sensor chip drivers. # obj-$(CONFIG_THERMAL) += thermal_sys.o thermal_sys-y += thermal_core.o #核心代码 # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o #dts解析 # governors 五种governor thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o # cpufreq cooling thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o # clock cooling thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o # devfreq cooling thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o # platform thermal drivers 平台相关代码,提供读取温度的接口 obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-y += samsung/ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_ST_THERMAL) += st/ obj-$(CONFIG_QCOM_TSENS) += qcom/ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
2020年10月14日
1,032 阅读
0 评论
0 点赞
2020-10-13
Android Binder机制(1)简介
[TOC]1. 概述Android系统中,每个应用程序是由Android的Activity,Service,Broadcast,ContentProvider这四剑客的中一个或多个组合而成,这四剑客所涉及的多进程间的通信底层都是依赖于Binder IPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案,当然也存在部分其他的IPC方式,比如Zygote通信便是采用socket。Binder作为Android系统提供的一种IPC机制,无论从事系统开发还是应用开发,都应该有所了解,这是Android系统中最重要的组成,也是最难理解的一块知识点,错综复杂。要深入了解Binder机制,最好的方法便是阅读源码,借用Linux鼻祖Linus Torvalds曾说过的一句话:Read The Fucking Source Code。2. Binder2.1 IPC原理从进程角度来看IPC机制每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行交互。2.2 Binder原理Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及Binder驱动,其中Service Manager用于管理系统中的各种服务。架构图如下所示:可以看出无论是注册服务和获取服务的过程都需要ServiceManager,需要注意的是此处的Service Manager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java)。ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,要掌握Binder机制,首先需要了解系统是如何首次启动Service Manager。当Service Manager启动之后,Client端和Server端通信时都需要先获取Service Manager接口,才能开始通信服务。图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。2.3 C/S模式BpBinder(客户端)和BBinder(服务端)都是Android中Binder通信相关的代表,它们都从IBinder类中派生而来,关系图如下:client端:BpBinder.transact()来发送事务请求;server端:BBinder.onTransact()会接收到相应事务。转载:http://gityuan.com/2015/10/31/binder-prepare/
2020年10月13日
1,020 阅读
0 评论
0 点赞
2020-10-12
C库函数--memset()函数
描述在定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构。在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”。每种类型的变量都有各自的初始化方法,memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思。原型该函数的原型为:# include <string.h> void *memset(void *str, int c, size_t n);参数:str -- 指向要填充的内存块。c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。n -- 要被设置为该值的字符数。函数的功能是:将指针变量 s 所指向的前 n 字节的内存单元用一个“整数” c 替换,注意 c 是 int 型。str 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化。返回值:该值返回一个指向存储区 str 的指针。memset() 的作用是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。一般的变量如 char、int、float、double 等类型的变量直接初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦。当然,数组也可以直接进行初始化,但 memset 是对较大的数组或结构体进行清零初始化的最快方法,因为它是直接对内存进行操作的。这时有人会问:“字符串数组不是最好用'\0'进行初始化吗?那么可以用 memset 给字符串数组进行初始化吗?也就是说参数 c 可以赋值为'\0'吗?”可以的。虽然参数 c 要求是一个整数,但是整型和字符型是互通的。但是赋值为 '\0' 和 0 是等价的,因为字符 '\0' 在内存中就是 0。所以在 memset 中初始化为 0 也具有结束标志符 '\0' 的作用,所以通常我们就写“0”。memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。注意,如果是对指针变量所指向的内存单元进行清零初始化,那么一定要先对这个指针变量进行初始化,即一定要先让它指向某个有效的地址。而且用memset给指针变量如p所指向的内存单元进行初始化时,n 千万别写成 sizeof(p),这是新手经常会犯的错误。因为 p 是指针变量,不管 p 指向什么类型的变量,sizeof(p) 的值都是 4。注意,C语言中的指针和数组名不完全等价,不能将它们混为一谈。下面写一个程序:# include <stdio.h> # include <string.h> int main(void) { int i; //循环变量 char str[10]; char *p = str; memset(str, 0, sizeof(str)); //只能写sizeof(str), 不能写sizeof(p) for (i=0; i<10; ++i) { printf("%d\x20", str[i]); } printf("\n"); return 0; }根据memset函数的不同,输出结果也不同,分为以下几种情况:memset(p, 0, sizeof(p)); //地址的大小都是4字节 0 0 0 0 -52 -52 -52 -52 -52 -52 memset(p, 0, sizeof(*p)); //*p表示的是一个字符变量, 只有一字节 0 -52 -52 -52 -52 -52 -52 -52 -52 -52 memset(p, 0, sizeof(str)); 0 0 0 0 0 0 0 0 0 0 memset(str, 0, sizeof(str)); 0 0 0 0 0 0 0 0 0 0 memset(p, 0, 10); //直接写10也行, 但不专业 0 0 0 0 0 0 0 0 0 0
2020年10月12日
41 阅读
0 评论
0 点赞
2020-10-10
thermal参考内容
thermal框架分析,一个可以收藏的网站:https://www.pianshen.com/article/3202573108/https://www.cnblogs.com/arnoldlu/p/11207142.html#thermal_set_governorLinux Thermal,可以参考:https://blog.csdn.net/m0_37166404/article/details/80524096高通Thermal debughttps://blog.csdn.net/shichaog/article/details/60959260https://www.pianshen.com/article/7370972170/https://happybevis.github.io/2018/04/17/New-Themal-Flow-In-Android-O/thermal,电源管理几个图参考,持续关注:https://blog.csdn.net/yuzhongqingsi/category_9480031.htmlhttps://blog.csdn.net/agave7/article/details/105140240/linux cpu 电源管理分析:http://www.wowotech.net/pm_subsystem/cpu_core_pm_overview.htmlhidl的理解:https://blog.csdn.net/lf12345678910/article/details/103275402android framework目录结构:https://blog.csdn.net/w2064004678/article/details/106352161
2020年10月10日
939 阅读
0 评论
0 点赞
2020-10-09
Linux内核中断机制
[TOC]1. 中断的概念中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。当 CPU 收到一个中断 (IRQ)的时候,会去执行该中断对应的处理函数(ISR)。普通情况下,会有一个中断向量表,向量表中定义了 CPU 对应的每一个外设资源的中断处理程序的入口,当发生对应的中断的时候, CPU 直接跳转到这个入口执行程序。也就是中断上下文。(注意:中断上下文中,不可阻塞睡眠)。2. Linux中断 top/bottom玩过 MCU 的人都知道,中断服务程序的设计最好是快速完成任务并退出,因为此刻系统处于被中断中。但是在 ISR 中又有一些必须完成的事情,比如:清中断标志,读/写数据,寄存器操作等。在 Linux 中,同样也是这个要求,希望尽快的完成 ISR。但事与愿违,有些 ISR 中任务繁重,会消耗很多时间,导致响应速度变差。Linux 中针对这种情况,将中断分为了两部分:上半部(top half):收到一个中断,立即执行,有严格的时间限制,只做一些必要的工作,比如:应答,复位等。这些工作都是在所有中断被禁止的情况下完成的。底半部(bottom half):能够被推迟到后面完成的任务会在底半部进行。在适合的时机,下半部会被开中断执行。3. 中断处理程序驱动程序可以使用接口:request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)参数含义irq表了该中断的中断号,一般 CPU 的中断号都会事先定义好。handler中断发生后的 ISRflags中断标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)name中断相关的设备 ASCII 文本,例如 "keyboard",这些名字会在 /proc/irq 和 /proc/interrupts 文件使用dev用于共享中断线,传递驱动程序的设备结构。非共享类型的中断,直接设置成为 NULL中断标志flag的含义:标志含义IRQF_DISABLED设置这个标志的话,意味着内核在处理这个 ISR 期间,要禁止其他中断(多数情况不使用这个)IRQFSAMPLE_RANDOM表明这个设备产生的中断对内核熵池有贡献IRQF_TIMER为系统定时器准备的标志IRQF_SHARED表明多个中断处理程序之间共享中断线。同一个给定的线上注册每个处理程序,必须设置这个调用request_irq成功执行返回0。常见错误是 -EBUSY,表示给定的中断线已经在使用(或者没有指定 IRQF_SHARED)注意:request_irq 函数可能引起睡眠,所以不允许在中断上下文或者不允许睡眠的代码中调用。Linux内核熵池:Linux内核采用熵来描述数据的随机性。熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。在信息学中,熵被用来表征一个符号或系统的不确定性,熵越大,表明系统所含有用信息量越少,不确定度越大。计算机本身是可预测的系统,因此,用计算机算法不可能产生真正的随机数。但是机器的环境中充满了各种各样的噪声,如硬件设备发生中断的时间,用户点击鼠标的时间间隔等是完全随机的,事先无法预测。Linux内核实现的随机数产生器正是利用系统中的这些随机噪声来产生高质量随机数序列。核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。理论上,熵池中的数据是完全随机的,可以实现产生真随机数序列。为跟踪熵池中数据的随机性,内核在将数据加入池的时候将估算数据的随机性,这个过程称作熵估算。熵估算值描述池中包含的随机数位数,其值越大表示池中数据的随机性越好。释放中断:const void *free_irq(unsigned int irq, void *dev_id)用于释放中断处理函数。注意:Linux 中的中断处理程序是无须重入的。当给定的中断处理程序正在执行的时候,其中断线在所有的处理器上都会被屏蔽掉,以防在同一个中断线上又接收到另一个新的中断。通常情况下,除了该中断的其他中断都是打开的,也就是说其他的中断线上的重点都能够被处理,但是当前的中断线总是被禁止的,故,同一个中断处理程序是绝对不会被自己嵌套的。4. 中断上下文与进程上下文不一样,内核执行中断服务程序的时候,处于中断上下文。中断处理程序并没有自己的独立的栈,而是使用了内核栈,其大小一般是有限制的(32bit 机器 8KB)。所以其必须短小精悍。同时中断服务程序是打断了正常的程序流程,这一点上也必须保证快速的执行。同时中断上下文中是不允许睡眠,阻塞的。中断上下文不能睡眠的原因是:1、 中断处理的时候,不应该发生进程切换,因为在中断context中,唯一能打断当前中断handler的只有更高优先级的中断,它不会被进程打断,如果在 中断context中休眠,则没有办法唤醒它,因为所有的wake_up_xxx都是针对某个进程而言的,而在中断context中,没有进程的概念,没 有一个task_struct(这点对于softirq和tasklet一样),因此真的休眠了,比如调用了会导致block的例程,内核几乎肯定会死。2、schedule()在切换进程时,保存当前的进程上下文(CPU寄存器的值、进程的状态以及堆栈中的内容),以便以后恢复此进程运行。中断发生后,内核会先保存当前被中断的进程上下文(在调用中断处理程序后恢复);但在中断处理程序里,CPU寄存器的值肯定已经变化了吧(最重要的程序计数器PC、堆栈SP等),如果此时因为睡眠或阻塞操作调用了schedule(),则保存的进程上下文就不是当前的进程context了.所以不可以在中断处理程序中调用schedule()。3、内核中schedule()函数本身在进来的时候判断是否处于中断上下文:if(unlikely(in_interrupt()))BUG();因此,强行调用schedule()的结果就是内核BUG。4、中断handler会使用被中断的进程内核堆栈,但不会对它有任何影响,因为handler使用完后会完全清除它使用的那部分堆栈,恢复被中断前的原貌。5、处于中断context时候,内核是不可抢占的。因此,如果休眠,则内核一定挂起。5. 举例比如 RTC 驱动程序 (drivers/char/rtc.c)。在 RTC 驱动的初始化阶段,会调用到 rtc_init 函数:module_init(rtc_init);在这个初始化函数中调用到了 request_irq 用于申请中断资源,并注册服务程序:static int __init rtc_init(void) { ... rtc_int_handler_ptr = rtc_interrupt; ... request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL) ... }RTC_IRQ 是中断号,和处理器绑定。rtc_interrupt 是中断处理程序:static irqreturn_t rtc_interrupt(int irq, void *dev_id) { /* * Can be an alarm interrupt, update complete interrupt, * or a periodic interrupt. We store the status in the * low byte and the number of interrupts received since * the last read in the remainder of rtc_irq_data. */ spin_lock(&rtc_lock); rtc_irq_data += 0x100; rtc_irq_data &= ~0xff; if (is_hpet_enabled()) { /* * In this case it is HPET RTC interrupt handler * calling us, with the interrupt information * passed as arg1, instead of irq. */ rtc_irq_data |= (unsigned long)irq & 0xF0; } else { rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); } if (rtc_status & RTC_TIMER_ON) mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); spin_unlock(&rtc_lock); wake_up_interruptible(&rtc_wait); kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); return IRQ_HANDLED; }每次收到 RTC 中断,就会调用进这个函数。6. 中断处理流程发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理程序,在vector_irq里面,最终会调用中断处理的总入口函数。C 语言的入口为 : asm_do_IRQ(unsigned int irq, struct pt_regs *regs)asmlinkage void __exception_irq_entry asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { handle_IRQ(irq, regs); }该函数的入参 irq 为中断号。asm_do_IRQ -> handle_IRQvoid handle_IRQ(unsigned int irq, struct pt_regs *regs) { __handle_domain_irq(NULL, irq, false, regs); }handle_IRQ ->\_\_handle_domain_irqint __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, bool lookup, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); unsigned int irq = hwirq; int ret = 0; irq_enter(); #ifdef CONFIG_IRQ_DOMAIN if (lookup) irq = irq_find_mapping(domain, hwirq); #endif /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (unlikely(!irq || irq >= nr_irqs)) { ack_bad_irq(irq); ret = -EINVAL; } else { generic_handle_irq(irq); } irq_exit(); set_irq_regs(old_regs); return ret; }这里请注意:先调用了 irq_enter 标记进入了硬件中断:irq\_enter是更新一些系统的统计信息,同时在\_\_irq_enter宏中禁止了进程的抢占。虽然在产生IRQ时,ARM会自动把CPSR中的I位置位,禁止新的IRQ请求,直到中断控制转到相应的流控层后才通过local_irq_enable()打开。那为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦流控层或驱动程序主动通过local_irq_enable打开了IRQ,而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq\_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理再调用 generic\_handle\_irq最后调用 irq_exit 删除进入硬件中断的标记\_\_handle_domain_irq -> generic_handle_irqint generic_handle_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (!desc) return -EINVAL; generic_handle_irq_desc(desc); return 0; } EXPORT_SYMBOL_GPL(generic_handle_irq);首先在函数 irq_to_desc 中根据发生中断的中断号,去取出它的 irq_desc 中断描述结构,然后调用 generic_handle_irq_desc:static inline void generic_handle_irq_desc(struct irq_desc *desc) { desc->handle_irq(desc); }这里调用了 handle_irq 函数。所以,在上述流程中,还需要分析 irq_to_desc 流程:struct irq_desc *irq_to_desc(unsigned int irq) { return (irq < NR_IRQS) ? irq_desc + irq : NULL; } EXPORT_SYMBOL(irq_to_desc);NR_IRQS 是支持的总的中断个数,当然,irq 不能够大于这个数目。所以返回 irq_desc + irq。irq_desc 是一个全局的数组:struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { .handle_irq = handle_bad_irq, .depth = 1, .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock), } };这里是这个数组的初始化的地方。所有的 handle_irq 函数都被初始化成为了 handle_bad_irq。细心的观众可能发现了,调用这个 desc->handle_irq(desc) 函数,并不是咱们注册进去的中断处理函数啊,因为两个函数的原型定义都不一样。这个 handle_irq 是 irq_flow_handler_t 类型,而我们注册进去的服务程序是 irq_handler_t,这两个明显不是同一个东西,所以这里我们还需要继续分析。6.1 中断相关的数据结构Linux中,与中断相关的数据结构有3个结构名称作用irq_descIRQ 的软件层面上的资源描述,用于描述IRQ线的属性与状态,被称为中断描述符。irqactionIRQ 的通用操作irq_chip对应每个芯片的具体实现,用于描述不同类型的中断控制器。irq_chip 是一串和芯片相关的函数指针,这里定义的非常的全面,基本上和 IRQ 相关的可能出现的操作都全部定义进去了,具体根据不同的芯片,需要在不同的芯片的地方去初始化这个结构,然后这个结构会嵌入到通用的 IRQ 处理软件中去使用,使得软件处理逻辑和芯片逻辑完全的分开。6.2 初始化Chip相关的IRQ众所周知,启动的时候,C 语言从 start_kernel 开始,在这里面,调用了和 machine 相关的 IRQ 的初始化 init_IRQ():asmlinkage __visible void __init start_kernel(void) { char *command_line; char *after_dashes; ..... early_irq_init(); init_IRQ(); ..... }在 init_IRQ 中,调用了 machine_desc->init_irq():void __init init_IRQ(void) { int ret; if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq) irqchip_init(); else machine_desc->init_irq(); if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { if (!outer_cache.write_sec) outer_cache.write_sec = machine_desc->l2c_write_sec; ret = l2x0_of_init(machine_desc->l2c_aux_val, machine_desc->l2c_aux_mask); if (ret && ret != -ENODEV) pr_err("L2C: failed to init: %d\n", ret); } uniphier_cache_init(); }machine_desc->init_irq() 完成对中断控制器的初始化,为每个irq_desc结构安装合适的流控handler,为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。这里初始化的时候回调用到具体的芯片相关的中断初始化的地方。int __init s5p_init_irq_eint(void) { int irq; for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++) irq_set_chip(irq, &s5p_irq_vic_eint); for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) { irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31); return 0; }而在这些里面,都回去调用类似于:void irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle, const char *name); irq_set_handler(unsigned int irq, irq_flow_handler_t handle) { __irq_set_handler(irq, handle, 0, NULL); } static inline void irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle) { __irq_set_handler(irq, handle, 1, NULL); } void irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle, void *data); 这些函数定义在 include/linux/irq.h 文件。是对芯片初始化的时候可见的 APIs,用于指定中断“流控”中的 :irq_flow_handler_t handle也就是中断来的时候,最后那个函数调用。中断流控函数,分几种,电平触发的中断,边沿触发的等:/* * Built-in IRQ handlers for various IRQ types, * callable via desc->handle_irq() */ extern void handle_level_irq(struct irq_desc *desc); extern void handle_fasteoi_irq(struct irq_desc *desc); extern void handle_edge_irq(struct irq_desc *desc); extern void handle_edge_eoi_irq(struct irq_desc *desc); extern void handle_simple_irq(struct irq_desc *desc); extern void handle_untracked_irq(struct irq_desc *desc); extern void handle_percpu_irq(struct irq_desc *desc); extern void handle_percpu_devid_irq(struct irq_desc *desc); extern void handle_bad_irq(struct irq_desc *desc); extern void handle_nested_irq(unsigned int irq);而在这些处理函数里,会去调用到 : handle_irq_event 比如:/** * handle_level_irq - Level type irq handler * @desc: the interrupt description structure for this irq * * Level type interrupts are active as long as the hardware line has * the active level. This may require to mask the interrupt and unmask * it after the associated handler has acknowledged the device, so the * interrupt line is back to inactive. */ void handle_level_irq(struct irq_desc *desc) { raw_spin_lock(&desc->lock); mask_ack_irq(desc); if (!irq_may_run(desc)) goto out_unlock; desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); /* * If its disabled or no action available * keep it masked and get out of here */ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { desc->istate |= IRQS_PENDING; goto out_unlock; } kstat_incr_irqs_this_cpu(desc); handle_irq_event(desc); cond_unmask_irq(desc); out_unlock: raw_spin_unlock(&desc->lock); }而这个 handle_irq_event 则是调用了处理,handle_irq_event_percpu:irqreturn_t handle_irq_event(struct irq_desc *desc) { irqreturn_t ret; desc->istate &= ~IRQS_PENDING; irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); raw_spin_unlock(&desc->lock); ret = handle_irq_event_percpu(desc); raw_spin_lock(&desc->lock); irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); return ret; }handle_irq_event_percpu->\_\_handle_irq_event_percpu-> [action->handler()]这里终于看到了调用 的地方了,就是咱们通过 request_irq 注册进去的函数7. /proc/interrupts这个 proc 下放置了对应中断号的中断次数和对应的 dev-name参考:Linux 中断之中断处理浅析
2020年10月09日
1,106 阅读
0 评论
0 点赞
2020-10-09
线程与对称处理器
1. 进程和线程进程的概念有两个特点,一是资源所有权。一个进程包括一个存放进程映像的虚拟地址空间;二是调度/执行。一个进程沿着通过一个或多个程序的一条执行路径(轨迹)执行。这两个特点是独立的,为了区分这两个特点,分派的单位通常称作线程,而拥有资源所有权的单位称为进程。1.1 多线程在多线程环境中,进程被定义成资源分配的单位和一个被保护的单位,与进程相关联的有:存放进程映像的虚拟地址空间受保护地对处理器、其他进程(用于进程间通信)、文件和I/O资源的访问。在一个进程中,可能有一个或多个线程,每个线程有:线程执行状态(运行、就绪等)在未运行时保存的线程上下文;从某种意义上看,线程可以被看做进程内的一个被独立地操作的程序计数器一个执行栈用于每个线程局部变量的静态存储空间与进程内的其他线程共享的对进程的内存和资源的访问。线程的优点:在一个已有进程中创建一个新线程比创建一个全新进程所需的时间要少许多。研究表明,在UNIX中,线程的创建比进程快10倍终止一个线程比终止一个进程花费的时间少同一个进程内线程间切换比进程间切换花费的时间少线程提高了不同的执行程序间通信的效率。在大多数操作系统中,独立进程间的通信需要内核的介入,以提供保护和通信所需要的机制。但是,由于同一个进程中的线程共享内存和文件,它们无需调用内核就可以互相通信。
2020年10月09日
794 阅读
0 评论
0 点赞
2020-09-29
并发性:互斥与同步
操作系统设计中的核心问题是关于进程和线程的管理。并发是所有问题的基础,也是操作系统设计的基础。并发包括许多设计问题,其中有进程间通信、资源共享与竞争(例如内存、文件、I/O访问)、多个进程活动的同步以及分配给进程的处理器时间等。和并发相关的一些关键术语:原子操作一个或多个指令的序列,对外是不可分的;即没有其他进程可以看到其中间状态或者中断此操作临界区是一段代码,在这段代码中进程将访问共享资源,当另外一个进程已经在这段代码中运行时,这个进程就不能在这段代码中执行。死锁两个或两个以上的进程因其中的每个进程都在等待其他进程做完某些事情而不能继续执行,这样的情形叫做死锁活锁两个或两个以上进程为了响应其他进程中的变化而持续改变自己的状态但不做有用的工作,这样的情形叫做活锁互斥当一个进程在临界区访问共享资源时,其他进程不能进入该临界区访问任何共享资源,这种情形叫做互斥竞争条件多个线程或者进程在读写一个共享数据时,结果依赖于它们执行的相对时间,这种情形叫做竞争饥饿是指一个可运行的进程尽管可能继续执行,但被调度器无限期地忽视,而不能被调度执行的情况1.并发的原理在单处理器系统的情况下,出现问题的原因是中断可能会在进程中任何地方停止指令的执行;在多处理器系统的情况下,不仅同样的条件可以引发问题,而且当两个进程同时执行并且都试图访问同一个全局变量时,也会引发问题。这两类问题的解决方案是相同的:控制对共享资源的访问1.1 竞争条件竞争条件发生在多个进程或线程读写数据时,其最终的结果依赖于多个进程的指令执行顺序。1.2 操作系统关注的问题并发会带来哪些设计和管理问题?操作系统必须能够记住各个活跃的进程操作系统必须为每个进程分配和释放各种资源。操作系统必须保护每个进程的数据和物理资源,避免其他进程的无意干涉一个进程的功能和输出结果必须与执行速度无关。1.3 进程的交互知道程度关系一个进程对其他进程的影响潜在的控制问题进程之间不知道对方的存在竞争1.一个进程的结果与其他进程的活动无关 2.进程的执行时间可能会受影响互斥、死锁(可复用的资源)、饥饿进程间接知道对方的存在(如共享对象)通过共享合作1.一个进程的结果可能依赖于从其他进程获得的信息 2.进程的执行时间可能会受到影响互斥、死锁(可复用的资源)、饥饿、数据一致性进程直接知道对方的存在(它们有可用的通信原语)通过通信合作1.一个进程的结果可能依赖于从其他进程获得的信息2.进程的计时可能会受到影响死锁(可消费的资源)、饥饿2. 信号量现在讨论操作系统和用于提供并发性的程序设计语言机制。基本原理:两个或多个进程可以通过简单的信号进行合作,一个进程可以被迫在某一位置停止,直到它接收到一个特定的信号。任何复杂的合作需求都可以通过适当的信号结构得到满足,为了发信号,需要使用一个被称作信号量的特殊变量。为通过信号量s发送信号,进程可执行原语semSignal(s)(V操作);为通过信号量s接收信号,进程可执行原语semWait(s)(P操作);如果相应的信号仍然没有发送,则进程被挂起,直到发送完为止。为了达到预期的效果,可以把信号量看做是一个具有整数值的变量,在它之上定义三个操作:1)一个信号量可以初始化成非负数2)semWait操作使信号量减1。如果值变成负数,则执行semWait的进程被阻塞。否则进程继续执行。3)semSignal操作使信号量加1。如果值小于或者等于零,则被semWait操作阻塞的进程被解除阻塞。除了这三种操作外,没有任何其他方法可以检查或操作信号量。解释:开始时,信号量的值为零或正数。如果该值为正数,则该值等于发出semWait操作后可立即继续执行的进程的数量。如果该值为零(或者由于初始化,或者由于有等于信号量初值的进程已经等待),则发出semWait操作的下一个进程会被阻塞,此时该信号量的值变为负值。之后,每个后续的semWait操作都会使信号量的负值更大。该负值等于正在等待接触阻塞的进程的数量。在信号量为负值的情形下,每一个semSignal操作都会将等待进程中的一个进程解除阻塞。在信号量为负值的情形下,每一个semSignal操作都会将等待进程中的一个进程解除阻塞。2.1 互斥使用信号量s解决互斥问题的方法。设有n个进程,用数组P(i)表示,所有的进程都需要访问共享资源。每个进程中进入临界区前执行semWait(s),如果s的值为负,则进程被挂起;如果值为1,则s被减为0,进程立即进入临界区;由于s值不再为正,因而其他任何进程都不能进入临界区。/* program mutualexeclusion */ const int n = /* 进程数 */; semaphore s = 1; void P(int i) { while(true){ semWait(s); /* 临界区 */ semSignal(s); /* 其他部分 */ } } void main() { parbegin(P(1), P(2), ..., P(n)); }
2020年09月29日
836 阅读
0 评论
0 点赞
2020-09-24
c语言的左移和右移
https://www.cnblogs.com/lyggqm/p/7826938.html
2020年09月24日
853 阅读
0 评论
0 点赞
1
...
34
35
36
37