精华内容
下载资源
问答
  • 内核包括子系统

    千次阅读 2017-08-24 14:40:54
    内核包括子系统:Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。进程调度(SCHED)控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的...

    内核包括的子系统:

    Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。


    进程调度(SCHED)

    控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际上是仅等待CPU资源的进程,如果某个进程在等待其它资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调度算法选择新的进程。

    内存管理(MM)

    允许多个进程安全的共享主内存区域。Linux的内存管理支持虚拟内存,即在计算机中运行的程序,其代码,数据,堆栈的总量可以超过实际内存的大小,操作系统只是把当前使用的程序块保留在内存中,其余的程序块则保留在磁盘中。必要时,操作系统负责在磁盘和内存间交换程序块。内存管理从逻辑上分为硬件无关部分和硬件有关部分。硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关的部分为内存管理硬件提供了虚拟接口。

    虚拟文件系统

    (Virtual File System,VFS)隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。

    网络接口(NET)

    提供了对各种网络标准的存取和各种网络硬件的支持。网络接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动程序。

    进程间通讯(IPC)

    支持进程间各种通信机制。

    示意图如下

    image

    展开全文
  • 内核时间子系统的配置

    千次阅读 2016-06-02 13:26:30
    内核中的时间子系统主要向上层提供两种功能,一个就是时间功能,另一个就是timer定时器功能。时间功能: 时间功能是由内核中的timekeeper模块来负责维护,该模块需要clocksource的支持。内核中的模块划分为如下: ...

    内核中的时间子系统主要向上层提供两种功能,一个就是时间功能,另一个就是timer定时器功能。

    时间功能:
    时间功能是由内核中的timekeeper模块来负责维护,该模块需要clocksource的支持。内核中的模块划分为如下:
    Timekeeper—>clocksource—>hardware

    定时器功能:
    从原理上说,定时器需要有一个定期到来的tick来驱动它运行,每个tick到来时检查一下定时器是否到时,由此来实现定时器的功能。所以定时器的实现可以分为tick device设备和基于tick device的上层timer定时器实现。在内核中的模块划分如下所示:
    timer—>tick device—>clockevent—>hardware

    基于tick device的timer定时器还分为两种类型,一种是低精度timer,一种是高精度timer。
    低精度timer是旧的内核中的实现,一直延续至今,也就是我们熟悉的基于jiffies系统滴答来实现的,旧的内核中只有这种定时器实现。
    高精度timer是为了满足新的需求而诞生的hrtimer。高精度基本可以满足任何情况下的需求了,但是由于历史原因低精度timer不能直接从内核中删除,必须保持兼容性。
    一个CPU内可能有多个local clock硬件,但是只会有一个被设置为该CPU的tick设备,每个CPU有且只有一个tick设备。

    内核配置

    1、通用clock source和clock event的内核配置

    (1)clock event是为tick设备提供支持的模块,它分为了新版和旧版两种架构。新架构采用的是通用模型,旧架构采用的是平台相关的实现。
    相关的配置项为:CONFIG_GENERIC_CLOCKEVENTS和CONFIG_GENERIC_CLOCKEVENTS_BUILD。
    如果配置了这两个选项,就会使用新的时间子系统的构架,如果不配置,将使用旧的时间子系统架构。 这一项的配置一般是在arch中选择的。比如arch/arm/Kconfig中,而其余的需要根据情况在menuconfig中配置,对应的Kconfig为kernel/kernel/time/Kconfig。

    arch/arm/Kconfig
    config ARCH_OMAP1
        bool "TI OMAP1"
        depends on MMU
        select ARCH_HAS_CPUFREQ
        select ARCH_HAS_HOLES_MEMORYMODEL
        select ARCH_OMAP
        select ARCH_REQUIRE_GPIOLIB
        select CLKDEV_LOOKUP
        select CLKSRC_MMIO
        select GENERIC_CLOCKEVENTS---选择通用时间框架
        select GENERIC_IRQ_CHIP
        select HAVE_CLK
        select HAVE_IDE
        select IRQ_DOMAIN
        select NEED_MACH_IO_H if PCCARD
        select NEED_MACH_MEMORY_H
        help
          Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx)
    

    如上所示,定义ARCH_OMAP1的时候就已经select GENERIC_CLOCKEVENTS作为时间子系统的框架。

    (2)clocksource是为timekeeper提供支持的模块,目前的内核都是使用新的通用框架。不过为了兼容性考虑,kernel也保留了CONFIG_ARCH_USES_GETTIMEOFFSET这个配置项.

    2、tick device的配置

    如果选择了新的时间子系统的软件架构(配置了CONFIG_GENERIC_CLOCKEVENTS),那么内核会打开Timers subsystem的配置选项,主要是和tick以及高精度timer配置相关。

    tick相关的配置:

    CONFIG_HZ_PERIODIC
    

    无论何时,都启用周期性的tick,即便是在系统idle的时候。

    CONFIG_NO_HZ_IDLE
    

    Idle dynticks system (tickless idle)。在系统idle的时候,停掉tick。使能该选项会自动使能CONFIG_NO_HZ_COMMON选项。

    CONFIG_NO_HZ_FULL
    

    Full dynticks system (tickless)。即便在非idle的状态下,也就是说cpu上还运行在task时,也可能会停掉tick,这个选项和实时应用相关。使能该选项会自动使能CONFIG_NO_HZ_COMMON选项。

    上面的三个选项只能是配置其一,上面描述的是新的内核配置方法。对于旧的内核,只有CONFIG_NO_HZ一个配置项,用来配置idle dynticks system(非idle时有周期性tick,idle时无周期性tick),为了兼容旧的系统,新的内核仍然支持了这个选项。

    除此之外还有一个用来配置tick模式的选项:

    CONFIG_TICK_ONESHOT
    

    如果配置了这个选项,就决定了系统中所有的tick设备都是oneshot mode,否则就是periodic mode。
    当配置了CONFIG_NO_HZ_COMMON或者CONFIG_HIGH_RES_TIMERS的时候,这个选项就会一起被配置上,也就是说采用了dynticks或者hrtimer的系统中,它的tick设备模式必须是oneshot类型的。

    3、timer模块的配置

    CONFIG_HIGH_RES_TIMERS
    

    和高精度timer相关的配置只有一个的配置项。如果配置了高精度hrtimer,那么就会自动配置上CONFIG_TICK_ONESHOT,表示系统只支持one-shot类型的tick device。

    4、 time配置示例

    选择一个时间子系统的构架:
    (1)新的通用时间子系统软件框架(配置了CONFIG_GENERIC_CLOCKEVENTS)
    (2)传统时间子系统软件框架(不配置CONFIG_GENERIC_CLOCKEVENTS,配置CONFIG_ARCH_USES_GETTIMEOFFSET)
    除非维护一个旧的系统,否则建议用新的通用时间子系统框架了,这时候可能的配置包括如下几种情况:
    (1)使用低精度timer和不停的tick(HZ_PERIODIC)
    这种是旧的处理方式
    (2)使用低精度timer和dynamic tick(CONFIG_NO_HZ_IDLE)
    (3)使用高精度timer和不停的tick(HZ_PERIODIC)
    (4)使用高精度timer和dynamic tick(CONFIG_NO_HZ_IDLE)
    这种是时下较流行的配置
    注:上面描述的dynamic tick指的是Idle dynticks system。我们不考虑Full dynticks system的情况。
    采用了dynticks或者hrtimer的系统中,它的tick设备模式必须是oneshot类型的,也就是说上面4种情况,只有第1种情况下的tick 是周期模式的,其他三种都是oneshot mode。

    展开全文
  • 本篇介绍密钥管理子系统,只涉及内核如何管理密钥,涉及内核加密算法的实现。密钥本质上是一段数据,内核对它的管理有些类似对文件的管理。但是因为Linux内核不愿意让密钥像文件那样“静态”存储在磁盘或者其他...

    本篇介绍密钥管理子系统,只涉及内核如何管理密钥,不涉及内核加密算法的实现。密钥本质上是一段数据,内核对它的管理有些类似对文件的管理。但是因为Linux内核不愿意让密钥像文件那样“静态”存储在磁盘或者其他永久性存储介质上,所以内核对密钥的管理又有些像对进程的管理,有创建、实例化、删除等操作。

    先从系统中可见的伪文件系统proc看起,内核密钥管理在proc文件系统中创建了两个只读文件:/proc/keys和/proc/key-users。它们没有被创建在/proc/pid目录下,而是被直接创建在了proc文件系统的根目录下。这就造成了进程根本无法查看到别的进程的密钥。keys文件列出当前进程可查看的密钥,所以不同的进程读出的内容会不同。列出的内容包括序列号、过期时间、访问允许位、uid、gid、类型、描述等。key-users列出密钥的统计信息,包括uid、使用计数、密钥总数量和实例化数量、密钥数量的配额信息、密钥占用内存的配额信息。

    密钥

    密钥在内核代码中称为key,因为key是由用户态进程创建,由内核管理,其实体存储在内核申请的内存中,所以密钥管理需要实施配额管理。密钥有对称密钥和非对称密钥两大类,每类密钥又有很多种。密钥种类不同,payload中数据的格式和长度也不同。所以key数据结构中包含了数据成员type,其类型为key_type,其中包含若干函数指针,用于处理payload。主要内容可以参见include/linux/key-type.h。

    include/linnux/key.h
    struct key {
    	...
    	key_serial_t serial;       //内核通过此序列号来唯一地标识一个key
    	struct key_user *user;     //包含一些和用户配额相关的数据,像用户拥有key的数量,用户拥有key占用总内存之类
    	kuid_t uid;                //标识key的属主
    	kgid_t gid;                //标识key的属组
    	key_perm_t perm;           //key的访问权限
    	void *security;            //被LSM使用,指向的内容由具体的LSM模块定义
    	
    	unsigned short datalen;    //标识payload的长度
    	union {
    		union{
    			unsigned long value;
    			void __rcu *rcudata;
    			void *data;
    			void *data2[2];
    		} payload;
    		struct assoc_array keys;
    	};
    	
    	union {
    		struct keyring_index_key index_key;
    		struct {
    			struct key_type *type;
    			char *description;   //字符串,用于用户态进程查询密钥。用户态先用该字符串从内核查询到key对应的serial,后面直接用serial对key进行操作
    		};
    	};
    	unsigned long flags;         //包含密钥的状态信息和密钥的生命周期有关
    	...
    };
    
    include/linux/key_type.h
    struct key_type {
    	const char* name;
    	...
    	int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
    	int (*update)(struct key *key, struct key_preparsed_payload *prep);
    	int (*match)(const struct key *key, const void *desc);
    	void (*revoke)(struct key *key);
    	...
    };
    

    flags用于存放密钥的状态信息。密钥是动态创建的,它是有生命周期。用户态进程首先会创建密钥,内核响应用户态进程的请求会生成一个密钥,分配内存。密钥的第二个状态是instantiated,内核将用户态进程提供的输入填入密钥的负载(payload)之中。这时的密钥就可以被用来做加解密使用。密钥的死亡状态有三种。revoke和invalidate都是由用户态进程发起的请求,内核对invalidate的响应是立即让密钥失效,收回密钥上的资源;内核对revoke的响应也是让密钥失效并收回资源,只是在此之前先调用密钥所属的类型(struct key_type)中定义的一个函数(revoke)。dead状态不是由用户态进程删除密钥导致的,而是由于一种类型(key_type)的密钥失效导致的。

    		-------------
    		| allocated |   -----> user_construct
    		-------------                 /      |
    		        |                    /       |
    		        |                  /         |
    		       \/               \ /          |
    		----------------                    \/
    		| instantiated | <-----     negatice
    		----------------      
    		/        |        \
    	/            |            \
    \/              \/              \/
    revoked  dead       mvalidated
    

    密钥的构造由用户态进程发起,密钥的payload数据由用户态进程提供,或者由用户态进程指令内核生成。当内核子系统要用到某个密钥,而这个密钥还不存在怎么办?一种简单的做法是由内核启动一个用户态进程,再由这个进程来填充密钥的payload。在发起新进程之前,内核首先分配一个密钥,将密钥的状态设置为user_construct。发起的新进程负责填充密钥的payload。这时,进程有两个选择:一个是立刻提供payload并通知内核将密钥的状态置为instantiated;另一个是不能马上提供payload数据,它就通知内核将密钥的状态置为negative,以后再提供数据并修改状态。

    security/keys/request_key.c
    struct key *request_key(struct key_type *type, const char *description, const char *callout_info){
    	struct key *key;    // 定义密钥结构体变量
    	size_t callout_len = 0;
    	int ret;
    
    	if (callout_info){
    		callout_len = strlen(callout_info);
    		// request_key_and_link会尝试查找密钥,如果没有找到,request_key_and_link会调用construct_key_and_link
    		key = request_key_and_link(type, description, callout_info, callout_len, NULL, NULL, KEY_ALLOC_IN_QUOTA);
    		if (!IS_ERR(key)){
    			ret = wait_for_key_construction(key, false);
    			if (ret < 0){
    				key_put(key);
    				return ERR_PTR(ret);
    			}
    		}
    	}
    	return key;
    }
    

    密钥类型

    钥匙环keyring
    钥匙环可以包含若干密钥,当然这些密钥可以是另一个钥匙环。寻找一个密钥时,需要配合参数type,也就是说,不同类型的密钥在不同的名字空间中。比如一个类型为trusted的密钥和一个类型为user的密钥可以同名(严格地说,不是名字,是描述description),不会引起冲突。而是,一个密钥可以链接到多个钥匙环,密钥在不同的钥匙环中,其名字(描述)总是一样的。keyring使用key结构体类型的assoc_array成员,非keyring使用payload。

    struct key {
    	...
    	union {
    		union{
    			unsigned long value;
    			void __rcu *rcudata;
    			void *data;
    			void *data2[2];
    		} payload;
    		struct assoc_array keys;
    	};
    

    密钥挂在钥匙环上,钥匙环可以再挂在另一个钥匙环上。当用户态进程要找一个钥匙,该从哪个钥匙环开始?文件系统有一个根目录,根目录是文件查找的起点。钥匙环也类似,钥匙环有若干个特殊的ID,供用户态进程查找。

    ID 含义
    -1 线程keyring
    -2 进程keyring
    -3 会话keyring
    -4 用户ID对应的keyring
    -5 用户会话keyring
    -6 组ID对应的keyring
    -7 request_key操作中认证密钥(auth_key)的keyring
    -8 request_key目的keyring

    每个线程有一个自己的钥匙环,每个进程有一个自己的钥匙环。会话概念的引入和登录(login)过程有关,用户登录系统,就是启动了一次会话,这次登录的进程,及后续子孙进程共享同一个会话id。现在通过字符终端tty登录Linux还是这样的情况。简而言之,一个会话就是一组进程,它们共享一些资源,比如会话钥匙环。
    用户ID对应的钥匙环和组ID对应的钥匙环,脱离进程而存在。用户会话钥匙环主要用在登录程序,用户登录系统,输入用户名和口令,登录程序启动新进程(一般是一个shell),同时启动一个新会话,这个新进程的会话钥匙环就先设置为此次登录的用户的用户会话钥匙环

    user类型的密钥由用户态进程创建,并且一般是用户态进程使用此种类型密钥。logon类型和user类型很相似,主要区别在于进程可以写入logon类型密钥的负载,但是不能读出logon类型密钥的负载。logon类型密钥的负载存储的是用户名和口令。内核中的一些子系统,比如cifs,会使用这些信息。

    asymmetric对应非对称密钥,非对称密钥有两个密钥:公钥和私钥,公钥存储在payload成员中,私钥存储在type_data中。

    encrypted这种类型的密钥之所以命名为encrypted,原因是用户态进程只能读到加密后的密钥数据,因此用户态进程是无法使用这种密钥的。这种密钥是由内核中的程序使用的,如ecryptfs和IMA。用来加密encrypted密钥数据的密钥有两种,一种是前面提到的user类型的密钥,另一种是后面要提到的trusted类型的密钥。内核中的密钥,是由用户态进程动态创建的,这里,encryted类型的密钥的设计的初衷就是不允许用户态接触到明文存储的密钥数据,那么,用户态进程又该怎么创建这种密钥呢?答案是,创建这种密钥时的payload是一个字符串,其中包含一个指令,内核根据该指令来创建密钥。指令语法:

    1. “new [format] key-type:master-key-name keylen” -创建密钥,密钥长度为keylen,使用类型为key-type,名字为master-key-name的密钥作为此次创建的密钥的加密密钥。format有两种形式:deflaut和ecryptfs。加密密钥的类型可以是trusted或者user。
    2. “load hex_blob”-根据hex_blob的值来创建密钥,hex_blob是一个hex字符串,字符串本身是有格式的,其中包含用于加密的密钥的类型和名字、哈希校验值及加密后的密钥数据。一般用法是创建一个encrypted密钥,将其内容读出导入一个文件,在每次系统启动时根据文件内容创建密钥。比如:
      cat /etc/keys/kmk | keyctl padd user kmk @u
      keyctl add encrypted evm-key "load `cat /etc/keys/evm-key` " @u
    3. “update key-type:master-key-name”-改变用于加密密钥的密钥。用户修改密钥的负载(payload),负载字符串是上面这个格式时,encrypted类型的密钥的加密密钥就会被更改。

    trusted这种类型的密钥和TPM相关。由TPM硬件生成一个密钥,并存储在TPM硬件中。同encrypted类型,创建密钥时的payload是一个指令字符串。语法是:
    “new keylen [options]”
    “load hex_blob [pcrlock=pcrnum]”

    系统调用

    密钥管理子系统添加了三个系统调用:

    1. key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t keyring)
      创建成功后,新密钥会被链接入参数keyring表示的钥匙环中
    2. long keyctl(int cmd, …) …后续参数由cmd取值决定
      cmd的取值有:KEYCTL_GET_KEYRING_ID、KEYCTL_JOIN_SESSION_KEYRING、KEYCTL_UPDATE、KEYCTL_REVOKER、KEYCTL_DESCRIBE、KEYCTL_CLEAR、KEYCTL_LINK、KEYCTL_UNLINK、KEYCTL_SEARCH、KEYCTL_READ、KEYCTL_CHOWN、KEYCTL_SETPERM、KEYCTL_INSTANTIATE、KEYCTL_INSTANTIATE_IOV、KEYCTL_NEGATE、KEYCTL_REJECCT、KEYCTL_SET_TIMEOUT、KEYCTL_INVALIDATE、KEYCTL_GET_SECURITY、KEYCTL_GET_PERSISTENT、KEYCTL_SET_REQKEY_KEYRING、KEYCTL_ASSUME_AUTHORITY
    3. key_serial_t request_key(const char *type, const char *description, const char *callout_info, key_serial_t keyring)
      用户态进程通过这个系统调用让内核查询一个密钥,并将其链接入参数指定的钥匙环。如果这个密钥已经存在,则这个系统调用的功能和keyctl(KEYCTL_SEARCH, …)几乎没有区别。如果这个密钥不存在,在这个系统调用中内核还要负责创建密钥。那么,密钥的pauload在哪里?内核的做法是根据参数callout_info启动一个用户态进程,由这个用户态进程来具体创建并实例化这个密钥。实际中的问题是内核启动的这个进程本身可能还是无法创建密钥,它又要启动别的进程来做这个工作。在这种重复委托的情况下,有两个东西必须以某种方式传递:一个是密钥,这个密钥已经在内核中创建了,需要用户态进程来实例化;另一个是钥匙环,就是这个密钥在成功实例化后,需要被链接到哪一个钥匙环中。还有一个额外的数据,就是进程的凭证,helper 1或者helper 2或者helper n进程要为process 1进程实例化密钥,这些helper可能要查找process 1的一些和密钥相关的数据。内核引入了一种新的密钥类型:key_type_request_key_auth。这种类型的密钥不是用来加密数据的,而是用来在进程间传递实例化一个密钥所需的信息。在内核创建进程helper 1的时候,先创建一个key_type_request_key_auth类型的密钥,链接入helper 1进程的会话钥匙环。key_type_request_key_auth类型的密钥的payload的子成员data本是void指针,其所指的实例的类型为request_key_auth。
    struct request_key_auth{
    	struct key *target_key;
    	struct key *dest_keyring;
    	const struct cred *cred;
    	void *callout_info;
    	size_t callout_len;
    	pid_t pid;
    }
    

    helper 1进程启动后就可以调用keyctl(KEYCTL_ASSUME_AUTHORITY, …),将这个key_type_request_key_auth类型的密钥置入自己的进程数据结构的request_key_auth。内核在做密钥相关的查找时,也会查找与request_key_auth中cred相关的密钥。

    密钥访问类型 详情
    Read 读出一个密钥的payload;读出一个钥匙环下所有链接的信息
    Write 写入一个密钥的payload;在一个钥匙环中添加链接或删除链接
    View 查看一个密钥的类型、描述等属性
    Search 查找一个钥匙环,在搜索一个钥匙环时只会递归搜索其下有搜索访问权限的子钥匙环
    Link 允许一个密钥或钥匙环被链接入一个钥匙环
    Set Attribute 允许修改密钥的uid、gid、访问许可信息
    展开全文
  • Linux内核之pinctrl子系统

    千次阅读 2018-09-16 11:01:37
    pinctrl子系统以及DT机制出现之后,由于GPIO管理的特殊性,并没有将GPIO子系统合并到pinctrl子系统中,而是在pinctrl子系统为GPIO子系统保留了特殊的访问通道,已达到GPIO子系统访问pin的需求。 2.1. pinctrl-core ...

    1. 前言

    众所周知,ARM SoC提供了十分丰富的硬件接口,而接口物理上的表现就是一个个的pin(或者叫做pad, finger等)。为了实现丰富的硬件功能,SoC的pin需要实现复用功能,即单独的pin需要提供不同功能,例如,pin0既可以作为GPIO,可以也用于i2c的SCL,通过pin相关的复用寄存器来切换不同的功能。除此之外,软件还可以通过寄存器配置pin相关的电气特性,例如,上拉/下拉、驱动能力、开漏等。

    Linux kernel 3.0之前的内核,对于pin的功能配置都是通过目标板的配置文件(arch/arm/mach-*)来初始化的,这种配置方式比较繁琐,十分容易出现问题(例如,pin的功能配置冲突)。所以,Linux kernel 3.0之后,实现了DT的板级配置信息管理机制,大大改善了对于pin的配置方式,随之一起实现的就是pinctrl子系统。

    pinctrl子系统主要负责以下功能:

    1. 枚举、命名通过板级DTS配置的所有pin;
    2. 对于pin实现复用功能;
    3. 配置pin的电器特性,例如,上拉/下拉、驱动能力、开漏等。;

    可见,pinctrl子系统地位相当于kernel全局的pin管理中心,kernel中所有需要pin资源的驱动、子系统都需要通过pinctrl子系统来申请、配置、释放。可将对于pin的操作来说,pinctrl子系统十分重要的。

    2. 软件框架

    对于不同的SoC,其对于pin管理方式可能不同,所以软件上对于pin的配置方式可能存在较大的差异。对此,pinctrl子系统"求同存异",将pin的管理方式进行了抽象,形成pinctrl-core抽象层,将具体SoC的pin controler隔离出去,形成pinctrl-driver抽象层,pinctrl-core和pinctrl-driver通过抽象接口进行通信。对于pinctrl-core的back-end,即各个需要用到pin的驱动,pinctrl子系统将其抽象为pinctrl-client。

    通过上面的软件抽象,pinctrl子系统可以很好的应对不同的SoC pin controler的管理需求,同样可以很好的为不同需要的驱动程序提供pin操作服务。下图简单示意一下pinctrl子系统的软件架构。
    这里写图片描述
    通过观察pinctrl子系统的软件框架图,可以发现一个问题,那就是GPIO子系统与pinctrl子系统的关系。理论上,GPIO子系统作为pinctrl子系统的使用者,其地位应该和普通的设备驱动没有差别,但是由于以下原因导致GPIO子系统与pinctrl子系统的功能出现了耦合:

    1. 早在kernel 3.0之前,GPIO子系统就已经出现了,其功能也比较明确,就是管理pin的GPIO功能;
    2. pinctrl子系统以及DT机制出现之后,由于GPIO管理的特殊性,并没有将GPIO子系统合并到pinctrl子系统中,而是在pinctrl子系统为GPIO子系统保留了特殊的访问通道,已达到GPIO子系统访问pin的需求。

    2.1. pinctrl-core

    pinctrl-core抽象层主要的功能就是提供三种服务:

    1. 为SoC pin controler drvier提供底层通信接口的能力;
    2. 为Driver提供访问pin的能力,即driver配置pin复用能、配置引脚的电气特性;
    3. 为GPIO子系统提供GPIO访问的能力;

    对于第一种服务来说,其实,对于pinctrl-core抽象层,底层的pin存在方式以及如何对其配置,其完全不会去关心。那么,pinctrl-core如何完成对于pinctrl-driver的控制呢?其实很简单,pinctrl-core与pinctrl-driver是通过pin controller descriptor进行通信的。该结构定义如下:

    /**
     * struct pinctrl_desc - pin controller descriptor, register this to pin
     * control subsystem
     * @name: name for the pin controller
     * @pins: an array of pin descriptors describing all the pins handled by
     *  this pin controller
     * @npins: number of descriptors in the array, usually just ARRAY_SIZE()
     *  of the pins field above
     * @pctlops: pin control operation vtable, to support global concepts like
     *  grouping of pins, this is optional.
     * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
     * @confops: pin config operations vtable, if you support pin configuration in
     *  your driver
     * @owner: module providing the pin controller, used for refcounting
     */
    struct pinctrl_desc {
    
    	/*pinctrl-driver属性*/
        const char *name;
        const struct pinctrl_pin_desc *pins;                                                                                                                                                                            
        unsigned int npins;
    
    	/*pinctrl-drive抽象接口*/
        const struct pinctrl_ops *pctlops;
        const struct pinmux_ops  *pmxops;
        const struct pinconf_ops *confops;
        struct module *owner;
    };
    

    pinctrl_desc其实对于pinctrl-driver的抽象,其包括了pinctrl-driver所有属性以及其具有的所有能力;这就是典型的面向对象编程的思想,pinctrl-core将pinctrl-driver抽象为pinctrl_desc对象,具体到SoC pinctrl-driver便是该对象一个实例。pinctrl-core通过该实例完成对于系统中所有pin的操作。但是,具体到pinctrl-driver如何完成pin的相关操作,pinctrl-core其实是不关心的。这就将pinctrl-driver的管理的复杂性进行了隔离,与之通信的唯一方式就是预先定义好的抽象接口。这样,不管pinctrl-driver如何变化,只要是按照协议,实例化pinctrl_desc,那么pinctrl-core就始终可以管理系统所有的pin。

    其实,对于软件设计最为本质的目的就是消除复杂性,面向对象编程其实是一种很好的解决软件复杂性的思想。不管是何种软件,服务器程序也好、Web前端程序也好亦或是嵌入式驱动程序也好,其面对的问题其实是一样的,那么最终解决问题指导思想也是相似的。最终目的,就是编写出复杂度低,易于维护的软件。

    2.2. pinctrl-driver

    pinctrl-driver主要为pinctrl-core提供pin的操作能力。对于具体的pinctrl-controler每个SoC的管理方式可能不同,对应到pinctrl-driver上,其实现方式可能会略有不同,但是,所有pinctrl-driver都是为了同一达到同一个目标,那就是把系统所有的pin信息以及对于pin的控制接口实例化成pinctrl_desc,并将pinctrl_desc注册到pinctrl-core中。

    pinctrl-driver对于系统pin的管理是通过function和group实现的。下面解释一下function和group的概念,解释之前需要提供一下pinctrl的DTS描述,对于DTS不是很熟悉的可以参考DTS相关的文章:

    / {  
    pinctrl: pinctrl@ff770000 {
        compatible = "rockchip,rk3288-pinctrl";
        reg = <0xff770000 0x140>,
              <0xff770140 0x80>,
              <0xff7701c0 0x80>;
        reg-names = "base", "pull", "drv";
        #address-cells = <1>; 
        #size-cells = <1>; 
        ranges;
    
        gpio0: gpio0@ff750000 {
            compatible = "rockchip,rk3288-gpio-bank0";
            reg =   <0xff750000 0x100>,
                <0xff730084 0x0c>,
                    <0xff730064 0x0c>,
                <0xff730070 0x0c>;
            reg-names = "base", "mux_bank0", "pull_bank0", "drv_bank0";
            interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&clk_gates17 4>;
    
            gpio-controller;
            #gpio-cells = <2>; 
    
            interrupt-controller;
            #interrupt-cells = <2>; 
        };
    
    	......
    
    	gpio0_i2c0 {
            i2c0_sda:i2c0-sda {
                rockchip,pins = <I2C0PMU_SDA>;
                rockchip,pull = <VALUE_PULL_DISABLE>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
            };
    
            i2c0_scl:i2c0-scl {
                rockchip,pins = <I2C0PMU_SCL>;
                rockchip,pull = <VALUE_PULL_DISABLE>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
            };
    
            i2c0_gpio: i2c0-gpio {
                rockchip,pins = <FUNC_TO_GPIO(I2C0PMU_SDA)>, <FUNC_TO_GPIO(I2C0PMU_SCL)>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
            };
        };
    

    上面的dts来自于Rockchip 3288的pinctrl配置dts,下面通过该配置,介绍一下function和group的概念:

    • group:所谓的group,如上dts中的i2c0_sda:i2c0_gpio,表示一组pins,这组pins统一表示了一种功能,比如,i2c需要两个pins表示,而spi需要四个引脚表示,而对于UART至少需要两个引脚表示。在定义pins的同时,还会提供对于每个pin的电气特性的配置,如,上下拉电阻、驱动能力等。
    • function:所谓的function,如上dts中的gpio0_i2c0,表示一当前这个pin所代表的的功能。每个function可以被一若干个group所引用,但是,对于每个独立的系统(BPS),只有一个group所引用的pin的function有效,否则会引起pin的function冲突。比如,一个pin既可以作为普通的gpio,也可以作为i2c的sda,那么,一个BPS,这个pin只能代表一个function,即,要么作为普通的gpio,作为i2c的sda。

    pinctrl-driver会在驱动的xxxx_probe函数中,将DTS中所定义关于function和group的配置,转换为pinctrl_desc中的数据属性,同时将pinctrl_desc中的对于pin相关操作的回调函数pctlops、pmxops、confops进行初始化,然后将pinctr_desc注册到pinctrl-core中。之后,pinctrl-driver所要做的工作就是静静的等待pinctrl-core的召唤。

    至于,pinctrl-driver如何转化pin信息以及pinctrl_desc的抽象接口的具体实现,每个SoC的具体实现各不相同,有兴趣的话可以参考具体的内核代码。

    2.3. pinctrl-client

    具体到使用系统pin资源的设备驱动程序,pinctrl-core主要提供为其提供两种能力:隶属于本设备的所有pin的function的配置能力和GPIO子系统对于GPIO的配置能力;

    2.2节中描述了pinctrl相关的DTS关于function和group的配置,对于具体的设备如何使用这些配置信息呢?还是以一个具体设备的DTS配置为例说明问题,DTS配置如下:

    i2c0: i2c@ff650000{                                                                                                                                                                                     
            compatible = "rockchip,rk30-i2c";
            reg = <0xff650000 0x1000>;
            interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
            #address-cells = <1>;
            #size-cells = <0>;
            pinctrl-names = "default", "gpio";
            pinctrl-0 = <&i2c0_sda &i2c0_scl>;
            pinctrl-1 = <&i2c0_gpio>;
            gpios = <&gpio0 GPIO_B7 GPIO_ACTIVE_LOW>,  <&gpio0 GPIO_C0 GPIO_ACTIVE_LOW>;
            clocks = <&clk_gates10 2>;
            rockchip,check-idle = <1>;
            status = "disabled";
        };
    

    上面的是关于i2c0控制器的设备配置信息,我们关心的是下面的配置信息:

    		pinctrl-names = "default", "gpio";
            pinctrl-0 = <&i2c0_sda &i2c0_scl>;
            pinctrl-1 = <&i2c0_gpio>;
    

    pinctrl-names表示i2c0控制器所处的两种状态,称为pin state, 即:default、gpio;其中,pinctrl-0对应于defaut状态下其关心的function和group,类似的,pinctrl-1对应于gpio状态下其关心的function和group。

    pinctrl-names所列出的各个状态与系统电源管理模块的联系比较紧密,由于电源管理的需要,系统可能处于不同的工作状态,相应的设备驱动提供pins不同的工作状态,其目的为了降低系统整体功耗,达到省电的需求,这种需求在消费电子产品中尤为重要。

    一般情况下,各个core-driver,例如i2c-core、spi-core会在调用设备驱动程序的probe初始化函数之前,将设备的工作状态设定为default状态。pinctrl-core的consumer.h文件(include/linux/pinctrl/consumer.h)文件提供了配置pin state的接口函数,其原型如下:

    extern struct pinctrl * __must_check pinctrl_get(struct device *dev);
    extern void pinctrl_put(struct pinctrl *p);
    extern struct pinctrl_state * __must_check pinctrl_lookup_state(
                                struct pinctrl *p,
                                const char *name);
    extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
    
    extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
    extern void devm_pinctrl_put(struct pinctrl *p);
    
    extern int pinctrl_pm_select_default_state(struct device *dev);
    extern int pinctrl_pm_select_sleep_state(struct device *dev);
    extern int pinctrl_pm_select_idle_state(struct device *dev);
    

    对于普通的设备驱动程序来说,一般不会使用到上述的接口,在涉及到电源管理或者子系统驱动程序(i2c-core、spi-core)可能用到上述接口。后续文档(GPIO 子系统、i2c-core-drvier、spi-core-drive)会详细分析。

    展开全文
  • 构建Linux内核驱动demo子系统示例

    千次阅读 2016-02-09 15:51:25
    本文参考内核RTC子系统并提取出一个简单的demo驱动子系统框架示例程序,可作为模板,适用于一些简单的具有类似多外设Linux设备驱动归一化开发。
  • Linux内核之mmc子系统-sdio

    万次阅读 2014-06-30 14:36:06
    现在的Linux内核中,mmc不仅是一个驱动,而是一个子系统。这里通过分析Linux3.2.0内核,结合TI的arm335x平台及omap_hsmmcd host分析下mmc子系统,重点关注sdio及架构在其上的具体sdio IP驱动实现。 1. General ...
  • Linux内核中的pinctrl子系统应用实例

    万次阅读 多人点赞 2017-05-31 10:48:23
    Linux内核中的pinctrl子系统应用实例  由于近期在做一个项目用到了pinctrl子系统,但是对pinctrl子系统了解又不是很多,所以遇到了麻烦,但是找度娘发现很少有同行对pinctrl的具体用法做出说明,所以只能自己去搞了...
  • 表示时间的结构 在内核当中有几个不同的结构用于表示时间。
  • I/O内核子系统

    2018-06-18 09:45:23
    像调度、缓冲、高速缓存、假脱机、设备预留以及错误处理是由内核I/O子系统提供的并且是建立在硬件和设备驱动程序结构之上的,I/O子系统还负责保护自己免受错误进程和恶意用户的危害 下面简单的介绍下I/O的调度 ...
  • Linux内核crypto子系统学习笔记

    千次阅读 2018-04-15 18:02:26
    crypto note v0.1 2017/12/19 Sherlock init v0.2 2017/3/25 Sherlock add sync/asyns code...本文分析Linux kernel里crypto子系统的大概实现,写crypto子系统下的加速器驱动的时候 可以参考下。crypto子系统支持...
  • Linux内核时间管理子系统——时钟源

    千次阅读 2014-08-27 22:17:13
    在Linux内核中有两种不同的clock设备,一种是clock source设备,另一种是clock event设备。
  • 命令包括: <div class="se-preview-section-delimiter"></div>
  • Linux内核SPI子系统架构分析

    千次阅读 2012-08-06 09:44:50
    SPI总线上有两类设备:一类是主控端,通常作为SOC系统的一个子模块出现,比如很多嵌入式MPU中都常常包含SPI模块。一类是受控端,例如一些SPI接口的Flash、传感器等等。主控端是SPI总线的控制者,通过使用SPI协议主动...
  • Linux内核由哪几个子系统组成

    千次阅读 2018-05-24 09:32:12
    *Linux内核由哪几个子系统组成?** Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。1.进程调度(SCHED):控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序...
  • i2c子系统内核中I2C子系统的结构

    千次阅读 2012-04-07 11:59:36
    本文开始,分析内核的i2c子系统。 说明:1.分析的内核版本为2.6.37.1  2.开发板为TQ2440,板载ARM9(S3C2440)  3.I2C设备为AT24C02  4.分析顺序就是内核I2C子系统的注册顺序(即本系列文章发表的先后顺序)。 ...
  • Linux内核工程导论——CGroup子系统

    千次阅读 2015-08-27 19:47:46
    cgroup子系统  cgroup是现代andriod的基础,最初提出也是andriod内部的人员提出的,后来被实现在linux内核内。通过cgroup可以将定额的系统资源(如CPU、内存等)分配给特定的一组进程。默认情况下编译内核时打开...
  • 内核中有一个rfkill子系统,使用这个可以关闭任何一个射频收发器。Linux中倾向于通用架构子系统,各个设备其实都是实现这个子系统规定的函数。这些子系统向上就提供操作同类函数的完整接口。这就是类似于面向对象...
  • 内核的网络子系统定义了rtnetlink,用做和用户空间的交互,rtnetlink为AF_NETLINK协议的一个类别NETLINK_ROUTE,其它类别包括NETLINK_XFRM、NETLINK_GENERIC等。renetlink主要注册了LINK、ROUTE、ADDRESS、NEI...
  • 内核启动过程中需要完成...这部分的代码往往是直接便如内核的而且是直接调用的另一种是非关键的的子系统(或者说模块、功能)的初始化,这部分根据配置可以加载,可以以built-in的方式编到内核的可执行文件中,也可以
  • 音频子系统 音频框架 音频设备是非常常用,但又最容易在linux下出现问题的设备之一。音频设备种类和芯片繁多,所以必须提供足够多的驱动,并且有内核有足够的驱动与设备的匹配能力。也正是由于多样性,内核必须...
  • 5.2.1 OS内核的I/O核心子系统及功能

    千次阅读 2020-05-15 22:10:02
    文章目录0.I/O核心子系统以及功能1.这些功能在哪个层次实现?假脱机技术I/O调度设备保护 0.I/O核心子系统以及功能 1.这些功能在哪个层次实现? 假脱机技术 I/O调度 设备保护
  • 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处... Linux内核主要由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS)、网络接口(NET)和进程间通信(IPC)5个子系统组成,如图1所示。
  •  Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0~MAX_INT之间的整数标识,能使用负数,gpio与硬件体系密切
  • 推荐博文: Linux内核“问题门”——学习问题、经验集锦推荐下载:《Linux内核修炼之道》精华版之方法论...不管是drivers/pci那儿的,还是arch/i386/pci那儿的,也都只属于一个PCI子系统,本着一个中国的原则,咱们要
  • 文件子系统使用一个缓冲机制存取文件数据,缓冲机制调节在内核与二级存储设备之间的数据流。缓冲机制同 块IO设备驱动程序交互,以便启动往内核去的数据传送及从内核来的数据传送。设备驱动程序是用来控制外围 设备...
  • Linux内核的5个子系统

    千次阅读 2014-03-13 21:17:44
    首先一张熟悉的图来说明GNU/linux的基本体系结构:  体系的上部分是用户(或应用程序)空间,这是用户应用程序执行的地方。...系统调用接口之下是内核代码,可以更精确地定义为独立于体系结构的内核
  • 操作系统与操作系统内核

    万次阅读 多人点赞 2017-07-06 10:39:57
    总的说来,一个操作系统包含内核(是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件)以及其他计算机系统所必须的组件(如函数库、编译器、调式工具、文本编辑器、网站服务器,以及一个Unix的...
  • 推荐博文: Linux内核“问题门”——学习...最后感谢清华女刘静,让我深刻体会到了素质教育的重要性,让我感到有责任写写子系统的初始化。各个子系统的初始化是内核整个初始化过程必然要完成的基本任务,这些任务按照
  • misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型。本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动,向上提供多...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 209,809
精华内容 83,923
关键字:

内核不包括的子系统是