精华内容
下载资源
问答
  • 因此,在中断驱动程序中,引入工作队列实现中断的分层,硬件处理在中断函数中处理,软件及其耗时操作则在Linux内核线程中实现。本小节,将介绍Linux内核队列及其使用方式。 29.2 工作队列特点 (1)工作队列中是...

    29.1 前言

    Linux中的工作队列是将工作推后执行的一种方式,通常用在中断分层机制中,因为中断函数的原则是(1)快;(2)不能睡眠或等待;因此,在中断驱动程序中,引入工作队列实现中断的分层,硬件处理在中断函数中处理,软件及其耗时操作则在Linux内核线程中实现。本小节,将介绍Linux内核队列及其使用方式。

    29.2 工作队列特点

    (1)工作队列中是可以阻塞或睡眠的;

    (2)调用schedule_work(&work)后,work马上就会被调度执行;

    (3)Linux工作队列属于一种异步处理机制;

    (4)工作队列是通过内核进程实现的;

                                           

    29.3 相关函数说明

    workqueue*create_workqueue(*name)

    创建工作队列;返回一个工作队列;name为工作队列名字

    destroy_workqueue(struct workqueue_struct *wq)

    销毁工作队列;wq为工作队列指针

    queue_work(*wq,*work)

    调度一个指定的工作;wq工作队列指针;work工作指针

    INIT_WORK(*work,callfunc)

    动态初始化工作;work为工作结构指针;callfunc为回调函数

    schedule_work(*work)

    工作调度;work为工作指针

    schedule_delayed_work(*work,delay)

    调度后延时执行;work工作指针;delay延时时间

    29.4 工作队列实例

    在驱动编写中,我们可以创建一个新的队列,也可以用内核已有的工作队列,这样我们就可以少去创建工作队列的代码,而直接用内核工作队列,这一般是满足我们的开发需要的。

    实例1:使用内核工作队列,由内核定时器模拟中断产生调度。

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/module.h>
    #include <linux/time.h>
    #include <linux/slab.h> //kmalloc头文件
    
    #define I_DEVICE_NAME			"iTimer"
    static int major;
    static struct timer_list tm;
    
    struct work_struct *work1;/*工作队列*/
    
    void iTimer_callback(unsigned long arg)
    {
    	struct timeval tv;
    	char *strp = (char*)arg;
    	printk(KERN_EMERG "%s: %lu, %s\n", __func__, jiffies, strp);
    	schedule_work(work1);
    
    	mod_timer(&tm,jiffies+6*HZ); //每6s模拟触发一次中断调度
    }
    
    //工作队列处理函数
    void work_CallBackfunc()
    {
    	printk(KERN_EMERG "this is second part work\n");	
    }
    
    static const struct file_operations stfops = {
    	.owner	= THIS_MODULE,
    };
    
    static int __init iTest_Init(void)
    {
    	/* 主设备号设置为0表示由系统自动分配主设备号 */
    	major = register_chrdev(0, I_DEVICE_NAME, &stfops);
    
    	/*Linux内核定时器初始化*/
    	init_timer(&tm);
    	tm.function= iTimer_callback;
    	tm.data = (unsigned long)"I am timer";
    	tm.expires = jiffies+1*HZ;
    	add_timer(&tm);
    
    	//创建工作队列
    	work1=kmalloc(sizeof(struct work_struct),GFP_KERNEL);  //第二个参数是标志
    	INIT_WORK(work1,work_CallBackfunc);
    
    	return 0;
    }
    
    static void __exit iTest_Exit(void)
    {
    	unregister_chrdev(major, I_DEVICE_NAME);
    	del_timer(&tm);
    }
    
    module_init(iTest_Init);
    module_exit(iTest_Exit);
    
    MODULE_LICENSE("GPL");
    

    实例2:创建新工作队列及构建工作。

    static struct workqueue_struct *miki_test_wq;//声明工作队列
    static struct work_struct miki_test_work;;//声明工作
    
    /*工作处理函数*/
    static void miki_test_work_callback ()
    {
        //这里添加相应的逻辑,比如每隔5秒让闪关灯亮一次等
    }
    
    /*主函数*/
    void mian()
    {
        //创建自己的工作队列
        miki_test_wq = create_singlethread_workqueue("miki_test");
        //初始化工作,实际上是让工作work绑定工作处理函数miki_test_work_callback
        INIT_WORK(&miki_test_work, miki_test_work_callback);
        //调度执行一个指定(miki_test_wq)中的任务miki_test_work
        queue_work(miki_test_wq, &miki_test_work); 
    }
    

     

     

     

    展开全文
  • linux内核配置实例

    2010-11-10 22:09:00
    第一部分:准备工作   Linux内核有着庞大的代码量,超过700万行,但它确实是一个非常灵活的操作系统。现在发行版linux已经具备很强的适应能力,能编译支持绝大部分设备(发行版)。但是可能你会有与...

    第一部分:准备工作

     

    Linux内核有着庞大的代码量,超过700万行,但它确实是一个非常灵活的操作系统。现在发行版linux已经具备很强的适应能力,能编译支持绝大部分设备(发行版)。但是可能你会有与大多数用户不同的需求,或者你有个不同的硬件。通过针对特定环境的定制,你可以使它更加匹配你的硬件平台。

     

    构建内核所需要的工具:

    编译器,链接器,make工具。编译器我们选择GCClinux内核主要由C语言和少量的汇编语言来编写。GCC版本不必要求过高,否则可能导致内核编译出错。

    连接器我们选择binutilsGCC并不能自己完成所有编译,binutils用来完成源文件的汇编和链接。make工具是一个编译管理工具,它能遍历内核源码树以确定需要进行编译的文件,然后调用编译器和其它构建所需要的工具完成内核的构建工作。内核的构建需要GUN版的make。查看这三个工具版本的命令分别是:gcc --version,ld -v,make --version

     

    使用内核所需要的工具:

    通常当前运行内核的版本不会影响任何用户的应用程序,但是对于个别程序,内核版本是至关重要的。如果你的内核已经升级,那么其中一些软件需要升级。

    Util-linux:

    它是一个小工具的集合,其中大多数工具用来控制磁盘分区的创建和挂载,并控制系统的硬件时钟。查看它的版本命令为:fdformat --version

    module-init-tools:

    如果需要使用linux内核模块,就要用到这个工具。内核模块是一个可以在内核运行时添加或删除得可加载代码块。把驱动程序编译成内核模块很有用,内核可以只加载与当前系统有关的驱动程序。为了根据当前硬件加载所需的驱动和选项,所有linux发行版本都使用模块,儿不是把所有内核中需要的驱动程序和选项都编译成一大块。这样就可以节省内存,只加载正确控制机器所需的机器代码。查看它的版本命令为:depmod -v

    文件系统相关工具:

    不同的文件系统,需要用不同的工具来创建,格式化,配置,修复磁盘分区。Util-linux软件包中包含其中一些工具,但是比较流行的文件系统有单独的软件包,其中包含操作这些文件系统的必要程序。用ext2/ext3/ext4来举例,它们被同样的工具管理,这个工具就是e2fsprogs软件包。查看它的版本命令为:tune2fs

     

    其他工具:

    udev是一个能让linux/dev目录下提供固定设备命名系统的程序。几乎所有的linux发行版都用udev来管理/dev目录,因此如果没有它计算机将无法启动。但是udev依赖于/sys目录的结构,这个结构随着内核的变化而变化,有些变化会损坏udev,使计算机无法启动。请尽量使用linux发行版的udev。查看它的版本命令为:udevinfo -V

    进程管理工具procps软件包,用于管理和监视进程的便利工具。

    PCMCIA工具用来配置linux下运行的PCMCIA设备。

     

    第二部分:操作实例

     

    在准备工作完成后,我们将进行下一步,源码的挑选。所有的linux内核源码都可以在kernel.org网站上找到。用wget下载2.6.30版内核,输入wget http://www.kernel.org/pub/linux/v2.6/linux-2.6.30.7.tar.bz2。现在合适的源码已经被下载,在主目录下建立一个名为linux的目录,来存放内核源码文件。

    mkdir ~/linux

    mv ~/linux-2.6.30.tar.bz2 ~/linux/

    进入linux目录,并解压内核源码。

    tar –xzvf linux-2.6.30.tar.bz2

     

    至此,我们已经将源码放置在合适的位置,接下来就该构建内核。首先选择适当的配置,然后编译内核。这两步都使用make工具完成。

     

    创建内核配置:

    内核配置保存在内核源码树顶级目录下名为.config的文件中。刚解压完的内核源码中没有.config文件,需要手动创建。

    cd linux-2.6.30

    make defconfig/*创建默认内核配置,使系统可以正常运转*/

    接下来我们需要根据自己的需要来修改配置。完成此项工作我们选择基于终端的配置工具menuconfig。工具虽然有了,但是我们并不知道用这个工具来编译哪些文件。下面我们将在内核中来查找针对我们自己的硬件的信息。插入我的usb转接头,键入命令:

    lsusb/*查看所有连接到系统的USB设备*/

    USB设备很容易移除,我们可以拔掉那个USB设备,然后再运行一遍lsusb命令,这样就可以确定以上哪条信息是针对你的新硬件的了。

    Bus 002 Device 003: ID 0403:6001 Future Technology Devices International,Ltd FT232 USB-Serial (UART) IC/*我的硬件识别信息*/

    其中ID 0403:6001这个信息对我们很有用处,我们需要用它来查找内核中与硬件匹配的信息。0403代表的是厂商ID,就是哪家厂商。6001是硬件ID

    下面开始用0403搜索内核源码树:

    grep -i -R  -l 0403 drivers/*

    该命令执行完后,会在屏幕上显示若干条以.data .c .h等为结尾的文件,比如drivers/net/wireless/XXXXX.X,不用看最后一部分,前三个目录名就可以确定这是个USB无线设备。同样的判断方法,我们就可以确定我们需要的内核文件了。以防万一,我们进入这个文件中,USB驱动告诉内核它们支持哪些谁被,以便内核可以把驱动绑定到设备上。一般在一个结构体变量中列出制造商ID和设备ID。如果我们设备的制造商ID和设备ID在里面的话,说明这个驱动支持我们的硬件设备。

    cd linux-2.6.30/*进入内核文件中*/

    find –type f –name Makefile | xargs grep XXXXX/*会显示一个以CONFIG_为前缀的字段*/

    找到这个字段后,返回内核Makefile文件中,使用内核配置工具menuconfig,搜索这个字段。最后在该程序菜单中相应位置启动这个驱动。

     

     

     

    第三部分:整体思路及总结

     

    下面我来说说我的整体思路。确定一个新设备由哪个驱动控制的最简单的方法,就是把内核源码树中所有此类驱动构建成模块,并让udev完成设备和驱动的匹配。同时,这些为设备匹配驱动的步骤取决于你所使用的设备类型。由于设备地不同,为其确定驱动的步骤也不尽相同。

     

    总的来说,查找特定USB设备驱动步骤是:

    1,找到需要驱动的USB设备制造商ID和产品ID,分别在添加和移除设备后使用lsusb来查看列表中发生的改变。

    2,在内核源码树种搜索USB设备制造商ID和产品ID。它们都应该在架构提struct usb_device_id中定义。

    3,使用findgrep在内核的Makefile文件中查找用于构建该模块的以CONFIG_为前缀的字段。find –type f –name Makefile | xargs grep DRIVER_NAME

    4,在内核配置系统中搜索这个配置的值,在菜单中进入到相应的位置并启用这个选项来构建内核驱动。

    展开全文
  • linux内核相关视频解析: 5个方面分析linux内核架构,让你对内核不再陌生 90分钟了解Linux内存架构,numa的优势,slab的实现,vmalloc的原理 手把手带你实现一个Linux内核文件系统 简介 作用是将应用层序的请求传递...

    linux内核相关视频解析:

    5个方面分析linux内核架构,让你对内核不再陌生
    90分钟了解Linux内存架构,numa的优势,slab的实现,vmalloc的原理
    手把手带你实现一个Linux内核文件系统

    简介

    作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接起源于该进程。virt/ ---- 提供虚拟机技术的支持。

    Linux内核预备工作

    理解Linux内核最好预备的知识点:

    懂C语言
    懂一点操作系统的知识
    熟悉少量相关算法
    懂计算机体系结构

    Linux内核的特点:

    结合了unix操作系统的一些基础概念

    在这里插入图片描述

    Linux内核的任务:

    1.从技术层面讲,内核是硬件与软件之间的一个中间层。作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。

    2.从应用程序的层面讲,应用程序与硬件没有联系,只与内核有联系,内核是应用程序知道的层次中的最底层。在实际工作中内核抽象了相关细节。

    3.内核是一个资源管理程序。负责将可用的共享资源(CPU时间、磁盘空间、网络连接等)分配得到各个系统进程。

    4.内核就像一个库,提供了一组面向系统的命令。系统调用对于应用程序来说,就像调用普通函数一样。

    内核实现策略:

    1.微内核。最基本的功能由中央内核(微内核)实现。所有其他的功能都委托给一些独立进程,这些进程通过明确定义的通信接口与中心内核通信。

    2.宏内核。内核的所有代码,包括子系统(如内存管理、文件管理、设备驱动程序)都打包到一个文件中。内核中的每一个函数都可以访问到内核中所有其他部分。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。

    哪些地方用到了内核机制?

    1.进程(在cpu的虚拟内存中分配地址空间,各个进程的地址空间完全独立;同时执行的进程数最多不超过cpu数目)之间进行通 信,需要使用特定的内核机制。

    2.进程间切换(同时执行的进程数最多不超过cpu数目),也需要用到内核机制。

    进程切换也需要像FreeRTOS任务切换一样保存状态,并将进程置于闲置状态/恢复状态。

    3.进程的调度。确认哪个进程运行多长的时间。

    Linux进程

    1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接起源于该进程。

    2.通过pstree命令查询。实际上得系统第一个进程是systemd,而不是init(这也是疑问点)

    3.系统中每一个进程都有一个唯一标识符(ID),用户(或其他进程)可以使用ID来访问进程。

    Linux内核源代码的目录结构

    Linux内核源代码包括三个主要部分:

    内核核心代码,包括第3章所描述的各个子系统和子模块,以及其它的支撑子系统,例如电源管理、Linux初始化等

    其它非核心代码,例如库文件(因为Linux内核是一个自包含的内核,即内核不依赖其它的任何软件,自己就可以编译通过)、固件集合、KVM(虚拟机技术)等

    编译脚本、配置文件、帮助文档、版权说明等辅助性文件

    使用ls命令看到的内核源代码的顶层目录结构,具体描述如下。

    include/ ---- 内核头文件,需要提供给外部模块(例如用户空间代码)使用。

    kernel/ ---- Linux内核的核心代码,包含了3.2小节所描述的进程调度子系统,以及和进程调度相关的模块。

    mm/ ---- 内存管理子系统(3.3小节)。

    fs/ ---- VFS子系统(3.4小节)。

    net/ ---- 不包括网络设备驱动的网络子系统(3.5小节)。

    ipc/ ---- IPC(进程间通信)子系统。

    arch// ---- 体系结构相关的代码,例如arm, x86等等。
    arch//mach- ---- 具体的machine/board相关的代码。
    arch//include/asm ---- 体系结构相关的头文件。
    arch//boot/dts ---- 设备树(Device Tree)文件。

    init/ ---- Linux系统启动初始化相关的代码。
    block/ ---- 提供块设备的层次。
    sound/ ---- 音频相关的驱动及子系统,可以看作“音频子系统”。
    drivers/ ---- 设备驱动(在Linux kernel 3.10中,设备驱动占了49.4的代码量)。

    lib/ ---- 实现需要在内核中使用的库函数,例如CRC、FIFO、list、MD5等。
    crypto/ ----- 加密、解密相关的库函数。
    security/ ---- 提供安全特性(SELinux)。
    virt/ ---- 提供虚拟机技术(KVM等)的支持。
    usr/ ---- 用于生成initramfs的代码。
    firmware/ ---- 保存用于驱动第三方设备的固件。

    samples/ ---- 一些示例代码。
    tools/ ---- 一些常用工具,如性能剖析、自测试等。

    Kconfig, Kbuild, Makefile, scripts/ ---- 用于内核编译的配置文件、脚本等。

    COPYING ---- 版权声明。
    MAINTAINERS ----维护者名单。
    CREDITS ---- Linux主要的贡献者名单。
    REPORTING-BUGS ---- Bug上报的指南。

    Documentation, README ---- 帮助、说明文档。

    【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)
    在这里插入图片描述

    Linux内核体系结构简析简析

    在这里插入图片描述

    图1 Linux系统层次结构

    最上面是用户(或应用程序)空间。这是用户应用程序执行的地方。用户空间之下是内核空间,Linux 内核正是位于这里。GNU C Library (glibc)也在这里。它提供了连接内核的系统调用接口,还提供了在用户空间应用程序和内核之间进行转换的机制。这点非常重要,因为内核和用户空间的应用程序使用的是不同的保护地址空间。每个用户空间的进程都使用自己的虚拟地址空间,而内核则占用单独的地址空间。

    Linux 内核可以进一步划分成 3 层。最上面是系统调用接口,它实现了一些基本的功能,例如 read 和 write。系统调用接口之下是内核代码,可以更精确地定义为独立于体系结构的内核代码。这些代码是 Linux 所支持的所有处理器体系结构所通用的。在这些代码之下是依赖于体系结构的代码,构成了通常称为 BSP(Board Support Package)的部分。这些代码用作给定体系结构的处理器和特定于平台的代码。

    Linux 内核实现了很多重要的体系结构属性。在或高或低的层次上,内核被划分为多个子系统。Linux 也可以看作是一个整体,因为它会将所有这些基本服务都集成到内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信、I/O、内存和进程管理,更具体的服务都是插入到微内核层中的。每种内核都有自己的优点,不过这里并不对此进行讨论。

    随着时间的流逝,Linux 内核在内存和 CPU 使用方面具有较高的效率,并且非常稳定。但是对于 Linux 来说,最为有趣的是在这种大小和复杂性的前提下,依然具有良好的可移植性。Linux 编译后可在大量处理器和具有不同体系结构约束和需求的平台上运行。一个例子是 Linux 可以在一个具有内存管理单元(MMU)的处理器上运行,也可以在那些不提供 MMU 的处理器上运行。

    Linux 内核的 uClinux 移植提供了对非 MMU 的支持。

    图2是Linux内核的体系结构
    在这里插入图片描述

    图2 Linux内核体系结构

    Linux内核的主要组件有:系统调用接口、进程管理、内存管理、虚拟文件系统、网络堆栈、设备驱动程序、硬件架构的相关代码。

    (1)系统调用接口
    SCI 层提供了某些机制执行从用户空间到内核的函数调用。正如前面讨论的一样,这个接口依赖于体系结构,甚至在相同的处理器家族内也是如此。SCI 实际上是一个非常有用的函数调用多路复用和多路分解服务。在 ./linux/kernel 中您可以找到 SCI 的实现,并在 ./linux/arch 中找到依赖于体系结构的部分。

    (2)进程管理
    进程管理的重点是进程的执行。在内核中,这些进程称为线程,代表了单独的处理器虚拟化(线程代码、数据、堆栈和 CPU 寄存器)。在用户空间,通常使用进程 这个术语,不过 Linux 实现并没有区分这两个概念(进程和线程)。内核通过 SCI 提供了一个应用程序编程接口(API)来创建一个新进程(fork、exec 或 Portable Operating System Interface [POSIX] 函数),停止进程(kill、exit),并在它们之间进行通信和同步(signal 或者 POSIX 机制)。

    进程管理还包括处理活动进程之间共享 CPU 的需求。内核实现了一种新型的调度算法,不管有多少个线程在竞争 CPU,这种算法都可以在固定时间内进行操作。这种算法就称为 O(1) 调度程序,这个名字就表示它调度多个线程所使用的时间和调度一个线程所使用的时间是相同的。O(1) 调度程序也可以支持多处理器(称为对称多处理器或 SMP)。您可以在 ./linux/kernel 中找到进程管理的源代码,在 ./linux/arch 中可以找到依赖于体系结构的源代码。

    (3)内存管理
    内核所管理的另外一个重要资源是内存。为了提高效率,如果由硬件管理虚拟内存,内存是按照所谓的内存页 方式进行管理的(对于大部分体系结构来说都是 4KB)。Linux 包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。不过内存管理要管理的可不止 4KB 缓冲区。Linux 提供了对 4KB 缓冲区的抽象,例如 slab 分配器。这种内存管理模式使用 4KB 缓冲区为基数,然后从中分配结构,并跟踪内存页使用情况,比如哪些内存页是满的,哪些页面没有完全使用,哪些页面为空。这样就允许该模式根据系统需要来动态调整内存使用。为了支持多个用户使用内存,有时会出现可用内存被消耗光的情况。由于这个原因,页面可以移出内存并放入磁盘中。这个过程称为交换,因为页面会被从内存交换到硬盘上。内存管理的源代码可以在 ./linux/mm 中找到。

    (4)虚拟文件系统
    虚拟文件系统(VFS)是 Linux 内核中非常有用的一个方面,因为它为文件系统提供了一个通用的接口抽象。VFS 在 SCI 和内核所支持的文件系统之间提供了一个交换层(请参看图4)。
    在这里插入图片描述

    图3 Linux文件系统层次结构

    在 VFS 上面,是对诸如 open、close、read 和 write 之类的函数的一个通用 API 抽象。在 VFS 下面是文件系统抽象,它定义了上层函数的实现方式。它们是给定文件系统(超过 50 个)的插件。文件系统的源代码可以在 ./linux/fs 中找到。文件系统层之下是缓冲区缓存,它为文件系统层提供了一个通用函数集(与具体文件系统无关)。这个缓存层通过将数据保留一段时间(或者随即预先读取数据以便在需要是就可用)优化了对物理设备的访问。缓冲区缓存之下是设备驱动程序,它实现了特定物理设备的接口。

    (5)网络堆栈
    网络堆栈在设计上遵循模拟协议本身的分层体系结构。回想一下,Internet Protocol (IP) 是传输协议(通常称为传输控制协议或 TCP)下面的核心网络层协议。TCP 上面是 socket 层,它是通过 SCI 进行调用的。socket 层是网络子系统的标准 API,它为各种网络协议提供了一个用户接口。从原始帧访问到 IP 协议数据单元(PDU),再到 TCP 和 User Datagram Protocol (UDP),socket 层提供了一种标准化的方法来管理连接,并在各个终点之间移动数据。内核中网络源代码可以在 ./linux/net 中找到。

    (6)设备驱动程序
    Linux 内核中有大量代码都在设备驱动程序中,它们能够运转特定的硬件设备。Linux 源码树提供了一个驱动程序子目录,这个目录又进一步划分为各种支持设备,例如 Bluetooth、I2C、serial 等。设备驱动程序的代码可以在 ./linux/drivers 中找到。

    (7)依赖体系结构的代码
    尽管 Linux 很大程度上独立于所运行的体系结构,但是有些元素则必须考虑体系结构才能正常操作并实现更高效率。./linux/arch 子目录定义了内核源代码中依赖于体系结构的部分,其中包含了各种特定于体系结构的子目录(共同组成了 BSP)。对于一个典型的桌面系统来说,使用的是 x86 目录。每个体系结构子目录都包含了很多其他子目录,每个子目录都关注内核中的一个特定方面,例如引导、内核、内存管理等。这些依赖体系结构的代码可以在 ./linux/arch 中找到。

    如果 Linux 内核的可移植性和效率还不够好,Linux 还提供了其他一些特性,它们无法划分到上面的分类中。作为一个生产操作系统和开源软件,Linux 是测试新协议及其增强的良好平台。Linux 支持大量网络协议,包括典型的 TCP/IP,以及高速网络的扩展(大于 1 Gigabit Ethernet [GbE] 和 10 GbE)。Linux 也可以支持诸如流控制传输协议(SCTP)之类的协议,它提供了很多比 TCP 更高级的特性(是传输层协议的接替者)。

    Linux 还是一个动态内核,支持动态添加或删除软件组件。被称为动态可加载内核模块,它们可以在引导时根据需要(当前特定设备需要这个模块)或在任何时候由用户插入。

    Linux 最新的一个增强是可以用作其他操作系统的操作系统(称为系统管理程序)。最近,对内核进行了修改,称为基于内核的虚拟机(KVM)。这个修改为用户空间启用了一个新的接口,它可以允许其他操作系统在启用了 KVM 的内核之上运行。除了运行 Linux 的其他实例之外, Microsoft Windows也可以进行虚拟化。惟一的限制是底层处理器必须支持新的虚拟化指令。

    Linux体系结构和内核结构区别

    1.当被问到Linux体系结构(就是Linux系统是怎么构成的)时,我们可以参照下图这么回答:从大的方面讲,Linux体系结构可以分为两块:

    (1)用户空间:用户空间中又包含了,用户的应用程序,C库

    (2)内核空间:内核空间包括,系统调用,内核,以及与平台架构相关的代码

    在这里插入图片描述

    2.Linux体系结构要分成用户空间和内核空间的原因:

    1)现代CPU通常都实现了不同的工作模式,

    以ARM为例:ARM实现了7种工作模式,不同模式下CPU可以执行的指令或者访问的寄存器不同:

    (1)用户模式 usr

    (2)系统模式 sys

    (3)管理模式 svc

    (4)快速中断 fiq

    (5)外部中断 irq

    (6)数据访问终止 abt

    (7)未定义指令异常

    以(2)X86为例:X86实现了4个不同级别的权限,Ring0—Ring3 ;Ring0下可以执行特权指令,可以访问IO设备;Ring3则有很多的限制

    2)所以,Linux从CPU的角度出发,为了保护内核的安全,把系统分成了2部分;

    3.用户空间和内核空间是程序执行的两种不同状态,我们可以通过“系统调用”和“硬件中断“来完成用户空间到内核空间的转移

    4.Linux的内核结构(注意区分LInux体系结构和Linux内核结构)
    在这里插入图片描述

    Linux驱动的platform机制

    Linux的这种platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。下面是SPI驱动层次示意图,Linux中的SPI总线可理解为SPI控制器引出的总线:
    在这里插入图片描述

    和传统的驱动一样,platform机制也分为三个步骤:

    1、总线注册阶段:
    内核启动初始化时的main.c文件中的kernel_init()→do_basic_setup()→driver_init()→platform_bus_init()→bus_register(&platform_bus_type),注册了一条platform总线(虚拟总线,platform_bus)。

    2、添加设备阶段:
    设备注册的时候Platform_device_register()→platform_device_add()→(pdev→dev.bus = &platform_bus_type)→device_add(),就这样把设备给挂到虚拟的总线上。

    3、驱动注册阶段:
    Platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(), 对在每个挂在虚拟的platform bus的设备作__driver_attach()→driver_probe_device(),判断drv→bus→match()是否执行成功,此时通过指针执行platform_match→strncmp(pdev→name , drv→name , BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver→probe(platform_device)。)开始真正的探测,如果probe成功,则绑定设备到该驱动。

    从上面可以看出,platform机制最后还是调用了bus_register() , device_add() , driver_register()这三个关键的函数。

    下面看几个结构体:

    struct platform_device           
    (/include/linux/Platform_device.h)
    {        
    const char    * name;        
    int        id;        
    struct device    dev;        
    u32        num_resources;        
    struct resource    * resource;
    };
    

    Platform_device结构体描述了一个platform结构的设备,在其中包含了一般设备的结构体struct device dev;设备的资源结构体struct resource * resource;还有设备的名字const char * name。(注意,这个名字一定要和后面platform_driver.driver àname相同,原因会在后面说明。)

    该结构体中最重要的就是resource结构,这也是之所以引入platform机制的原因。

    struct resource                            
    ( /include/linux/ioport.h)
    {        
    resource_size_t start;        
    resource_size_t end;        
    const char *name;        
    unsigned long flags;        
    struct resource *parent, *sibling, *child;
    };
    

    其中 flags位表示该资源的类型,start和end分别表示该资源的起始地址和结束地址(/include/linux/Platform_device.h):

    struct platform_driver              
    {        
    int (*probe)(struct platform_device *);        
    int (*remove)(struct platform_device *);        
    void (*shutdown)(struct platform_device *);        
    int (*suspend)(struct platform_device *, pm_message_t state);        
    int (*suspend_late)(struct platform_device *, pm_message_t state);        
    int (*resume_early)(struct platform_device *);        
    int (*resume)(struct platform_device *);        
    struct device_driver driver;
    };
    

    Platform_driver结构体描述了一个platform结构的驱动。其中除了一些函数指针外,还有一个一般驱动的device_driver结构。
    名字要一致的原因:

    上面说的驱动在注册的时候会调用函数bus_for_each_dev(), 对在每个挂在虚拟的platform bus的设备作__driver_attach()→driver_probe_device(),在此函数中会对dev和drv做初步的匹配,调用的是drv->bus->match所指向的函数。platform_driver_register函数中drv->driver.bus = &platform_bus_type,所以drv->bus->match就为platform_bus_type→match,为platform_match函数,该函数如下:

    static int platform_match(struct device * dev, struct device_driver * drv)   
    {       
    struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
    

    是比较dev和drv的name,相同则会进入really_probe()函数,从而进入自己写的probe函数做进一步的匹配。所以dev→name和driver→drv→name在初始化时一定要填一样的。

    不同类型的驱动,其match函数是不一样的,这个platform的驱动,比较的是dev和drv的名字,还记得usb类驱动里的match吗?它比较的是Product ID和Vendor ID。

    个人总结Platform机制的好处:

    1、提供platform_bus_type类型的总线,把那些不是总线型的soc设备都添加到这条虚拟总线上。使得,总线——设备——驱动的模式可以得到普及。

    2、提供platform_device和platform_driver类型的数据结构,将传统的device和driver数据结构嵌入其中,并且加入resource成员,以便于和Open Firmware这种动态传递设备资源的新型bootloader和kernel 接轨。

    Linux内核体系结构

    因为Linux内核是单片的,所以它比其他类型的内核占用空间最大,复杂度也最高。这是一个设计特性,在Linux早期引起了相当多的争论,并且仍然带有一些与单内核固有的相同的设计缺陷。

    在这里插入图片描述

    为了解决这些缺陷,Linux内核开发人员所做的一件事就是使内核模块可以在运行时加载和卸载,这意味着您可以动态地添加或删除内核的特性。这不仅可以向内核添加硬件功能,还可以包括运行服务器进程的模块,比如低级别虚拟化,但也可以替换整个内核,而不需要在某些情况下重启计算机。
    想象一下,如果您可以升级到Windows服务包,而不需要重新启动……

    内核模块

    如果Windows已经安装了所有可用的驱动程序,而您只需要打开所需的驱动程序怎么办?这本质上就是内核模块为Linux所做的。内核模块,也称为可加载内核模块(LKM),对于保持内核在不消耗所有可用内存的情况下与所有硬件一起工作是必不可少的。
    在这里插入图片描述

    模块通常向基本内核添加设备、文件系统和系统调用等功能。lkm的文件扩展名是.ko,通常存储在/lib/modules目录中。由于模块的特性,您可以通过在启动时使用menuconfig命令将模块设置为load或not load,或者通过编辑/boot/config文件,或者使用modprobe命令动态地加载和卸载模块,轻松定制内核。

    第三方和封闭源码模块在一些发行版中是可用的,比如Ubuntu,默认情况下可能无法安装,因为这些模块的源代码是不可用的。该软件的开发人员(即nVidia、ATI等)不提供源代码,而是构建自己的模块并编译所需的.ko文件以便分发。虽然这些模块像beer一样是免费的,但它们不像speech那样是免费的,因此不包括在一些发行版中,因为维护人员认为它通过提供非免费软件“污染”了内核。

    内核并不神奇,但对于任何正常运行的计算机来说,它都是必不可少的。Linux内核不同于OS X和Windows,因为它包含内核级别的驱动程序,并使许多东西“开箱即用”。希望您能对软件和硬件如何协同工作以及启动计算机所需的文件有更多的了解。
    在这里插入图片描述

    展开全文
  • Linux内核API 讲解 实例源代码sample 1.所有程序源代码均在 ubuntu9.04环境下,kernel 2.6.28-2.6.30上测试通过; 2.源码中部分注释为中文,如果出现乱码,请设置vi、vim、gedit等中文选项;如果忽略乱码,也不影响...
  • 我们在进行linux内核配置的时候经常会执行make menuconfig这个命令,然后屏幕上会出现以下界面: 这个界面是怎么生成的呢? 跟我们经常说的内核配置与与编译又有什么关系呢? 下面我们借此来讲解一下l...

    前面我们介绍模块编程的时候介绍了驱动进入内核有两种方式:模块和直接编译进内核,并介绍了模块的一种编译方式——在一个独立的文件夹通过makefile配合内核源码路径完成

        那么如何将驱动直接编译进内核呢?

        在我们实际内核的移植配置过程中经常听说的内核裁剪又是怎么麽回事呢?

    我们在进行linux内核配置的时候经常会执行make menuconfig这个命令,然后屏幕上会出现以下界面:

    这个界面是怎么生成的呢?

    跟我们经常说的内核配置与与编译又有什么关系呢?

    下面我们借此来讲解一下linux内核的配置机制及其编译过程。

    一、配置系统的基本结构

    Linux内核的配置系统由三个部分组成,分别是:

       1、Makefile:分布在 Linux 内核源代码根目录及各层目录中,定义 Linux 内核的编译规则;

        2、配置文件(config.in(2.4内核,2.6内核)):给用户提供配置选择的功能;

        3、配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和 make xconfig)。

       这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以。

    二、makefile menuconfig过程讲解

    当我们在执行make menuconfig这个命令时,系统到底帮我们做了哪些工作呢?

    这里面一共涉及到了一下几个文件我们来一一讲解

    Linux内核根目录下的scripts文件夹

    arch/$ARCH/Kconfig文件、各层目录下的Kconfig文件

    Linux内核根目录下的makefile文件、各层目录下的makefile文件

    Linux内核根目录下的的.config文件、arm/$ARCH/下的config文件

    Linux内核根目录下的 include/generated/autoconf.h文件

    1)scripts文件夹存放的是跟make menuconfig配置界面的图形绘制相关的文件,我们作为使用者无需关心这个文件夹的内容

     

    2)当我们执行make menuconfig命令出现上述蓝色配置界面以前,系统帮我们做了以下工作:

        首先系统会读取arch/$ARCH/目录下的Kconfig文件生成整个配置界面选项(Kconfig是整个linux配置机制的核心),那么ARCH环境变量的值等于多少呢?

    它是由linux内核根目录下的makefile文件决定的,在makefile下有此环境变量的定义:

    或者通过 make ARCH=arm menuconfig命令来生成配置界面,默认生成的界面是所有参数都是没有值的

        比如教务处进行考试,考试科数可能有外语、语文、数学等科,这里相当于我们选择了arm科可进行考试,系统就会读取arm/arm/kconfig文件生成配置选项(选择了arm科的卷子),系统还提供了x86科、milps科等10几门功课的考试题

     

    3)假设教务处比较“仁慈”,为了怕某些同学做不错试题,还给我们准备了一份参考答案(默认配置选项),存放在arch/$ARCH/configs下,对于arm科来说就是arch/arm/configs文件夹:

        此文件夹中有许多选项,系统会读取哪个呢?内核默认会读取linux内核根目录下.config文件作为内核的默认选项(试题的参考答案),我们一般会根据开发板的类型从中选取一个与我们开发板最接近的系列到Linux内核根目录下(选择一个最接近的参考答案)

    #cp arch/arm/configs/s3c2410_defconfig .config

    4).config

        假设教务处留了一个心眼,他提供的参考答案并不完全正确(.config文件与我们的板子并不是完全匹配),这时我们可以选择直接修改.config文件然后执行make menuconfig命令读取新的选项

        但是一般我们不采取这个方案,我们选择在配置界面中通过空格、esc、回车选择某些选项选中或者不选中,最后保存退出的时候,Linux内核会把新的选项(正确的参考答案)更新到.config中,此时我们可以把.config重命名为其它文件保存起来(当你执行make distclean时系统会把.config文件删除),以后我们再配置内核时就不需要再去arch/arm/configs下考取相应的文件了,省去了重新配置的麻烦,直接将保存的.config文件复制为.config即可.

    5)经过以上两步,我们可以正确的读取、配置我们需要的界面了

    那么他们如何跟makefile文件建立编译关系呢?

    当你保存make menuconfig选项时,系统会除了会自动更新.config外,还会将所有的选项以宏的形式保存在

    Linux内核根目录下的 include/generated/autoconf.h文件下

    内核中的源代码就都会包含以上.h文件,跟宏的定义情况进行条件编译。

    当我们需要对一个文件整体选择如是否编译时,还需要修改对应的makefile文件,例如:

        我们选择是否要编译s3c2410_ts.c这个文件时,makefile会根据CONFIG_TOUCHSCREEN_S3C2410来决定是编译此文件,此宏是在Kconfig文件中定义,当我们配置完成后,会出现在.config及autconf中,至此,我们就完成了整个linux内核的编译过程。

        最后我们会发现,整个linux内核配置过程中,留给用户的接口其实只有各层Kconfig、makefile文件以及对应的源文件。

        比如我们如果想要给内核增加一个功能,并且通过make menuconfig控制其声称过程

        首先需要做的工作是:修改对应目录下的Kconfig文件,按照Kconfig语法增加对应的选项;

        其次执行make menuconfig选择编译进内核或者不编译进内核,或者编译为模块,.config文件和autoconf.h文件会自动生成;

        最后修改对应目录下的makefile文件完成编译选项的添加;

        最后的最后执行make zImage命令进行编译。

    三、具体实例

    下面我们以前面做过的模块实验为例,讲解如何通过make menuconfig机制将前面单独编译的模块编译进内核或编译为模块

    假设我已经有了这么一个驱动:

    modules.c

    #include <linux/module.h> /*module_init()*/
    
    #include <linux/kernel.h> /* printk() */
    
    #include <linux/init.h> /* __init __exit */
    
    
    #define DEBUG //open debug message
    
    
    #ifdef DEBUG
    
    #define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
    
    #else
    
    #define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
    
    #endif
    
    
    /* Module Init & Exit function */
    
    static int __init myModule_init(void)
    
    {
    
        /* Module init code */
    
        PRINTK("myModule_init\n");
    
        return 0;
    
    }
    
    
    static void __exit myModule_exit(void)
    
    {
    
        /* Module exit code */
    
        PRINTK("myModule_exit\n");
    
        return;
    
    }
    
    
    module_init(myModule_init);
    
    module_exit(myModule_exit);
    
    
    MODULE_AUTHOR("xzy"); /*模块作者,可选*/
    
    MODULE_LICENSE("GPL"); /*模块许可证明,描述内核模块的许可权限,必须*/
    
    MODULE_DESCRIPTION("A simple Hello World Module"); /*模块说明,可选*/

    Step1:将modules.c拷到drivers/char/目录下(这个文件夹一般存放常见的字符驱动)

     

    Step2: vi driver/char/Kconfig,在

        config DEVKMEM后添加以下信息

     

    config MODULES
    tristate "modules device support"
    default y
    help
     Say Y here,the modules will be build in kernel.
     Say M here,the modules willbe build to modules.
     Say N here,there will be nothing to be do. 

     

    Step3:make menuconfig

         Device driver-character devices

               [*]modules device suppor

     

    Step4:vi driver/char/Makefile,在js-rtc后添加

    obj-$(CONFIG_MODULES)+= modules.o

    CONFIG_MODULES 必须跟上面的Kconfig中保持一致,系统会自动添加CONFIG_前缀

     

    modules.o必须跟你加入的.c文件名一致

    最后执行:make zImage modules就会被编译进内核中

     

    第三步:

     

    Step3:make menuconfig

         Device driver-character devices

               [M]modules device suppor

    把星号在配置界面通过空格改为M,最后执行make modules,在driver/char/目录下会生成一个modules.ko文件

    跟我们前面讲的单独编译模块效果一样,也会生成一个模块,将它考入开发板执行insmod moudles.ko,即可将生成的模块插入内核使用

    展开全文
  • Linux内核配置选项 (经典学习)

    万次阅读 2019-01-22 18:09:10
    2.5 Linux内核配置选项 下面以最新的Linux 2.6.20内核为例,介绍比较常用的一些Linux内核配置选项,其他选项读者可以参考系统提供的帮助信息。 需要说明的是,在内核配置中,某项选择Y表示把该项选择进内核,选择...
  • 一、linux体系结构 从大面上来说,linux体系结构分为: ...1、linux内核 内核是操作系统的核心,具有很多最基本功能,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能和稳定性。 系统
  • Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接...
  • 升级内核到最新版本/5.0.2 载入公钥和yum源 rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm yum install -y yum-plug...
  • Linux内核栈溢出分析

    2021-05-11 01:48:12
    Linux系统进程运行分为用户态和内核态,进入内核态之后使用的是内核栈,作为基本的安全机制,用户程序不能直接访问内核栈,所以尽管内核栈属于进程的地址空间,但与用户栈是分开的。内核栈需要方便快捷...
  • Linux配置并编译内核

    万次阅读 多人点赞 2018-08-03 16:29:36
    配置工具会询问许多问题并且允许开发者配置内核的每个方面。如果你有不确定的问题或者特性,你最好使用配置工具提供的默认值。本系列教程会使读者逐步了解配置内核的整个过程。   配置代码前需要在源文件的文件夹...
  • Linux内核配置参考

    千次阅读 2016-11-17 22:31:11
    Linux 2.6内核配置说明 (国嵌)    Code maturity level options  代码成熟度选项 Prompt for development and/or incomplete code/drivers 显示尚在开发中或尚未完成的代码与驱动.除非你是测试人员或者...
  • tid=15 简介 作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中...Linux内核就是基于这个策略实现的。Linux进程1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。...
  • Linux内核编译流程(Menuconfig图形化方式)Menuconfig配置内核原理:在Linux里面我们所看到的menuconfig界面是通过配置内核顶层的Kconfig产生的,而当输入make menuconfig命令的时候系统会读取Makefile来解析...
  • linux内核内存分配的方法 用户变量:用户空间定义的变量,地址为前3G 内核变量:内核空间定义的变量,地址为后1G1.1 kmalloc / kfree void *kmalloc(size_t size, gfp_t flags) 函数功能: 1.内核空间分配内存; 2....
  • 背景:由于exfat是常用的文件系统格式,而Linux由于版权的问题,没有在官方中添加...这里演示2种添加exfat格式的驱动,而不将驱动编译进内核。准备:下载exfat驱动:git clone https://github.com/arter97/exfat-lin...
  • Linux内核添加驱动

    2021-05-11 01:52:23
    Linux内核中提供了很多设备的驱动代码,但每个项目中总会需要添加我们自己的驱动,比如我们需要添加红外遥控驱动。我们可以先独立去编写和调试这个驱动,等成熟后应该放到内核目录树中,使用make modules命令统一...
  • Linux 内核配置机制(make menuconfig、Kconfig、makefile)讲解 前面我们介绍模块编程的时候介绍了驱动进入内核有两种方式:模块和直接编译进内核,并介绍了模块的一种编译方式——在一个独立的...
  • Linux内核高精度定时器hrtimer 使用实例   一、内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,以下学习使用hrtimer(high ...
  • linux内核启动过程分析

    千次阅读 2018-10-30 09:36:45
    kernel的Makefile比uboot的Makefile要复杂,在这里不需要一行一行的详细分析,只需要关注在配置编译过程需要用到的地方,其他的地方可以不管。 (1)Makefile开始定义了kernel的版本号,这个版本号很重要...
  • linux内核线程

    万次阅读 2018-10-16 18:22:49
    内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。实际上,内核线程只能由其他内核...
  • Linux内核配置系统

    2017-03-01 14:52:13
    配置系统的接本结果Linux内核配置系统由三个部分组成,分别是: 1. Makefile:分在内核源码中的Makefile,定义Linux内核的编译规则; 2. 配置文件(Config.in):给用户提供配置选择的功能; 3. 配置工具:包括...
  • 原子操作实例:     原子操作底层表现为一条汇编指令(ldrex、strex)。所以他们在执行过程中不会被别的代码路径所中断。     事务的原子性就是要么做完 要么不做。而如何...
  • linux共享上网设置 1、打开内核ip转发 vi /etc/sysctl.conf net.ipv4.ip_forward = 1 执行sysctrl -p生效  2、如果主机未启用防火墙,那么如下设置iptables [root@Web-Lnmp02 ~]# iptables -F [root@Web-Lnmp...
  • linux 内核库函数

    万次阅读 2020-04-08 18:06:46
    Linux内核也提供了与标准库函数功能相同的一些函数,但二者还是稍有差别。 类别 函数名 功能 函数形成 参数 描述 字符串转换 simple_strtol 把一个字符串转换为一个有符号长整数 long simple_strtol (const char * ...
  • linux内核添加新驱动实例,并提供menuconfig

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 111,026
精华内容 44,410
关键字:

linux内核配置实例

linux 订阅