zoukankan      html  css  js  c++  java
  • Linux内核基础设施

    1.前言

    本文主要介绍Linux内核实现的基本数据类型,包括链表,内核对象,内核对象引用计数,内核对象集合,

    2.链表

    1. 链表的基本结构

    内核链表可以将任何类型的数据结构连接起来,链表结构如下:

    1 struct list_head {
    2     struct list_head *next, *prev;
    3 };

     

    图 标准双链表

    典型的循环双向链表如上图所示。

    2. 链表相关API

    LIST_HEAD(list_name) 定义一个list_head结构体,next和pre成员均初始化为当前新创建的list_head
    
    list_add(new,head) 用于head元素之后紧接着插入new元素
    
    list_add_tail(new,head) 在head元素前面插入new元素,由于是循环链表,实际是将new元素插入到末尾
    
    list_del(entry) 从链表中删除一项 
    
    list_empty(head) 检查链表是否为空
    
    list_splice(list, head) 合并两个链表,把list插入到另一个链表head的后面
    
    list_entry(ptr, tpe, member) ptr是某数据结构中指向list_head成员的指针,type是该数据结构的类型,member是该数据结构中list_head成员变量名
    
    list_for_each(pos, head) 用于遍历链表的所有元素。pos表示链表的当前位置,head指定了表头

    3. 内核对象

    3.1 kobject

    1. kobject的基本结构

     1 struct kobject {
     2     const char        *name; /*kobject的名字*/
     3     struct list_head    entry;/*用于将kobject放置到一个链表中*/
     4     struct kobject        *parent;/*指向当前kobject的父对象,用于建立层次结构*/
     5     struct kset        *kset; /*用于将对象与其他对象放到一个集合中*/
     6     struct kobj_type    *ktype; /*提供了kobject的更多属性,最重要的是释放该数据结构资源的析构器函数*/
     7     struct kernfs_node    *sd; /* sysfs directory entry */
     8     struct kref        kref; /*用于简化引用计数*/
     9 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
    10     struct delayed_work    release;
    11 #endif
    12     unsigned int state_initialized:1;
    13     unsigned int state_in_sysfs:1;
    14     unsigned int state_add_uevent_sent:1;
    15     unsigned int state_remove_uevent_sent:1;
    16     unsigned int uevent_suppress:1;
    17 };

     注:kobject不是通过指针与其他数据结构连接,而必须直接嵌入到其他数据结构中,这样通过管理kobject达到了包含kobject对象的管理。由于kobject会嵌入到许多数据结构中,因此要保持kobject结构较小。

    2. kobject的API

    struct kobject *kobject_get(struct kobject *kobj) 增加kobject的引用计数
    
    void kobject_put(struct kobject *kobj) 减少kobject的引用计数
    
    void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 初始化kobject结构体,即将引用计数设为0,并初始化kobject的链表成员
    
    int kobject_add(struct kobject *kobj, struct kobject *parent, 将kobject加入sysfs中显示
            const char *fmt, ...)
    
    int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) kobject_init和kobject_add的合并操作
    
    struct kobject *kobject_create(void)   为一个kobject分配空间,并调用kobject_init初始化
    
    struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) kobject_create和kobject_add的合并操作
    
    static void kobject_cleanup(struct kobject *kobj) 在不需要kobject(以及包含kobject的对象)时释放kobject占用的资源

    3.2 kref

    1.kref结构体

    引用计数用来检测内核中有多少地方使用了某个对象,当内核一个部分包含某个对象的信息时,需要将该对象对应kobject的kref加1,不需要时则减1,如果减为0则释放该对象

    1 struct kref { 
    2     atomic_t refcount; /*原子计数,给出内核中当期使用某个对象的计数,在计数为0时,kobject就可以从内存中删除了*/
    3 };

     2.kref的API

    static inline void kref_init(struct kref *kref) 初始化kref,即将refcount初始化为1
    
    static inline void kref_get(struct kref *kref)  要使用某个对象必须将对应的kobject的引用计数加1
    
    static inline int kref_sub(struct kref *kref, unsigned int count,
    void (*release)(struct kref *kref))             对象不在使用则将对应的kref减1,如果引用计数为0则调用release释放kobject

     3.3 kset

    kset是kobject应用的第一个例子,因此它是用kobject 进行管理的,它与kset中包含的各个kobject无关,此处的kobject只是纯粹为了管理kset之需

    1 struct kset {
    2     struct list_head list; /*用于链接当前kset中所有的kobject的链表*/
    3     spinlock_t list_lock; 
    4     struct kobject kobj;
    5     const struct kset_uevent_ops *uevent_ops; /*提供了若干函数指针,用于将kset的相关信息透给应用层*/
    6 };

     3.4 ktype 

    1 struct kobj_type {
    2     void (*release)(struct kobject *kobj);
    3     const struct sysfs_ops *sysfs_ops;
    4     struct attribute **default_attrs;
    5     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
    6     const void *(*namespace)(struct kobject *kobj);
    7 };

     注:ktype与kset没有关系,kset已经提供了集合功能,ktype提供了与sysfs文件系统相关的接口。如果多个kobject导出类似的信息,则可以共享一个ktype提供所需要的方法

    4.  数据类型

    • 数据类型定义

    typedef  为避免依赖体系结构的相关特性,如sector_t, pid_t等

    • 字节序

    cpu_to_le64 将64位数据类型转换为小端序格式

    le64_to_cpu 将64位小端序格式转换为64位cpu端格式

    • per-cpu变量

    可有效避免多处理器并发访问同个变量引发的并发问题

    DEFINE_PER_CPU(name, type) name是变量名, type是数据类型

    get_cpu(name, cpu) 获取当前cpu上创建的变量实例

    smp_processor_id() 返回当前 cpu 的id

    • 访问用户空间

    __user 使用此标记来标识指向用户空间的指针

    5. 参考文档

    [1] 深入Linux内核架构(PLKA)

  • 相关阅读:
    第一次结对作业
    第一次博客作业
    个人总结
    第三次个人作业
    第二次结对作业
    第一次结对作业
    第一次个人编程作业
    第一次博客作业
    第三次个人作业——用例图设计
    第二次结对作业
  • 原文地址:https://www.cnblogs.com/smartjourneys/p/6681801.html
Copyright © 2011-2022 走看看