• 本博实时更新《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)的最新进展。 目前已经完成稿件。 2015年8月9日,china-pub开始上线预售: ... 2015年8月20日,各路朋友报喜...

    本博实时更新《Linux设备驱动开发详解(第3版)》的最新进展。 目前已经完成稿件。

    2015年8月9日,china-pub开始上线预售:

    http://product.china-pub.com/4733972

    2015年8月20日,各路朋友报喜说已经拿到了书。

    本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台

    本书微信公众号二维码, 如需联系请扫描下方二维码

    [F]是修正或升级;[N]是新增知识点;[D]是删除的内容

     

    第1章 《Linux设备驱动概述及开发环境构建》
    [D]删除关于LDD6410开发板的介绍
    [F]更新新的Ubuntu虚拟机
    [N]添加关于QEMU模拟vexpress板的描述

     

    第2章 《驱动设计的硬件基础》

    [N]增加关于SoC的介绍;
    [N]增加关于eFuse的内容;
    [D]删除ISA总线的内容了;
    [N]增加关于SPI总线的介绍;
    [N]增加USB 3.0的介绍;
    [F]修正USB同步传输方式英文名;
    [D]删除关于cPCI介绍;
    [N]增加关于PCI Express介绍;
    [N]增加关于Xilinx ZYNQ的介绍;
    [N]增加SD/SDIO/eMMC的章节;
    [D]删除“原理图分析的内容”一节;
    [N]增加通过逻辑分析仪看I2C总线的例子;

     

    第3章 《Linux内核及内核编程》

    [N]新增关于3.X内核版本和2015年2月23日 Linux 4.0-rc1
    [N]新增关于内核版本升级流程以及Linux社区开发模式讲解
    [N]新增关于Linux内核调度时间的图式讲解
    [N]新增关于Linux 3.0后ARM架构的变更的讲解
    [N]新增关于TASK_KILLABLE状态的简介
    [N]新增Linux内存管理图式讲解

    [F]修正Kconfig和Makefile中的一些表述
    [D]删除关于x86启动过程讲解
    [N]新增ARM Linux启动过程讲解
    [N]新增关于likely()和unlikely()讲解
    [N]新增toolchain的讲解,以及toolchain的几种浮点模式


    第4章 《Linux内核模块》
    [F]改正关于模块使用非GPL license的问题;
    [F]修正关于__exit修饰函数的内存管理

    第5章 《Linux文件系统与设备文件》
    [F]修正关于文件系统与块设备驱动关系图;
    [N]增加应用到驱动的file操作调用图;
    [N]增加通过netlink接受内核uevent的范例;
    [N]增加遍历sysfs的范例;

    [N]增加为kingston U盘编写udev规则的范例;
    [F]更新udev规则,以符合新版本;
    [N]增加udevadm的讲解;
    [N]高亮Android vold
     
    第6章 《字符设备驱动》
    [F]更新file_operations的定义,升级ioctl()原型;
    [N]增加关于Linux access_ok()的讲解以及Linux内核安全漏洞的说明;
    [F]修正globalmem的编码风格;
    [F]在globalmem支持2个以上实例的时候,从直接2个实例,升级为支持N个实例;

    第7章 《Linux设备驱动中的并发控制》
    [N]绘图深入讲解单核和多核下的各种竞态;
    [N]增加关于编译乱序,执行乱序,编译屏障和内存屏障的讲解;
    [N]增加关于ARM LDREX/STREX指令的讲解;
    [N]对spin_lock单核和多核的使用场景进行深入分析;

    [F]重新整理RCU的讲解方法和实例;
    [F]明确指明信号量已过时;
    [F]将globalmem中使用的信号量换为mutex。


    第8章 《Linux设备驱动中的阻塞与非阻塞I/O》
    [N]新增阻塞和非组塞的时序图
    [F]修正关于等待队列头部、等待队列元素的一些中文说法
    [N]添加等待队列的图形描述
    [F]修正globalfifo的编码风格
    [F]修正globalfifo可读可写的if判断为while判断
    [N]新增select的时序图
    [N]新增EPOLL的章节


    第9章 《Linux设备驱动中的异步通知与异步I/O》
    [F]修正关于glibc AIO支持
    [F]修正关于内核AIO支持
    [F]修正驱动AIO接口
    [D]删除关于驱动AIO支持的错误实例
    [N]高亮C10问题

    第10章 《中断与时钟》
    [N]增加关于ARM GIC的讲解
    [N]增加关于irq_set_affinity() API的讲解
    [N]增加关于devm_request_irq() API的讲解
    [N]增加关于request_any_context_irq() API的讲解

    [F]修正interrupt handler原型
    [F]修正work queue原型
    [N]新增关于Concurrency-managed workqueues讲解
    [N]增加关于ksoftirqd讲解
    [N]增加关于request_threaded_irq()讲解
    [D]删除s3c6410 rtc驱动中断实例
    [N]新增GPIO按键驱动中断实例
    [N]新增hrtimer讲解和实例
    [F]修正second设备编码风格

    第11章 《内存与I/O访问》
    [F]修正关于页表级数的描述,添加PUD
    [F]修正page table walk的案例,使用ARM Linux pin_page_for_write
    [N]新增关于ARM Linux内核空间虚拟地址分布
    [F]修正关于内核空间与用户空间界限
    [N]新增关于DMA、NORMAL和HIGHMEM ZONE的几种可能分布
    [N]新增关于buddy的介绍
    [F]修正关于用户空间malloc的讲解
    [N]增加mallopt()的案例
    [N]增加关于devm_ioremap、devm_request_region()和devm_request_mem_region()的讲解
    [N]增加关于readl()与readl_relaxed()的区别,writel()与writel_relaxed()的区别

    [F]更新vm_area_struct的定义
    [F]修正nopage() callback为fault() callback
    [N]增加io_remap_pfn_range()、vm_iomap_memory()讲解
    [F]强调iotable_init()静态映射目前已不太推荐
    [N]增加关于coherent_dma_mask的讲解
    [N]讲解dma_alloc_coherent()与DMA ZONE关系
    [N]提及了一致性DMA缓冲区与CMA的关系
    [N]增加关于dmaengine驱动和API的讲解

     

    第12章 《工程中的Linux设备驱动》
    [F]更名为《Linux设备驱动的软件架构思想》;
    [N]本章新增多幅图片讲解Linux设备驱动模型;
    [N]新增内容详细剖析为什么要进行设备与驱动的分离,platform的意义;
    [N]新增内容详细剖析为什么Linux设备驱动要分层,枚举多个分层实例;
    [N]新增内容详细剖析Linux驱动框架如何解耦,为什么主机侧驱动要和外设侧驱动分离;
    [N]DM9000实例新增关于在dts中填充平台信息的内容;
    [N]新增内容讲解驱动核心层的3大功能;
    [N]新增内容以面向对象类泛化对比Linux驱动;
    [N]SPI案例部分新增通过dts填充外设信息;

    [F]从tty, LCD章节移出架构部分到本章

     

    第13章 《Linux块设备驱动》
    [N]介绍关于block_device_operations的check_events成员函数
    [N]添加关于磁盘文件系统,I/O调度关系的图形
    [F]更新关于request_queue、request、bio、segment关系的图形
    [F]淘汰elv_next_request
    [F]淘汰blkdev_dequeue_request
    [N]添加关于blk_start_request描述
    [F]淘汰Anticipatory I/O scheduler
    [N]添加关于ZRAM块设备驱动实例
    [F]更新针对内核4.0-rc1的vmem_disk
    [N]添加关于vmem_disk处理I/O过程的图形
    [N]增加关于Linux MMC子系统的描述

     

    第14章 《Linux终端设备驱动》
    [D]整章全部删除,部分架构内容前移到第12章作为驱动分层实例

     

    第15章 《Linux I2C核心、总线与设备驱动》

    [F]修正i2c_adpater驱动的案例
    [N]增加关于在device tree中增加i2c设备的方法的描述

     

    第16章 《Linux网络设备驱动》

    [F]本章顺序从第16章前移到第14章
    [N]澄清sk_buff head、data、tail、end指针关系
    [F]更新sk_buff定义
    [F]澄清skb_put、skb_push、skb_reserve
    [N]增加netdev_priv的讲解,加入实例
    [N]增加关于get_stats()可以从硬件读统计信息的描述

    [F]修正关于net_device_stats结构体的定义位置
    [F]修正关于统计信息的更新方法

     

    第17章 《Linux音频设备驱动》

    [D] 本章直接删除

     

    第18章 《LCD设备驱动》

     

    [D] 本章直接删除,部分架构内容前移到第12章

     

    第19章 《Flash设备驱动》

    [D] 本章直接删除

     

    第20章 《USB主机与设备驱动》
    [F]前移到第16章;
    [F]更名为《USB主机、设备与Gadget驱动》;
    [N]增加关于xHCI的介绍;
    [F]修正usb gadget驱动为function驱动;
    [D]删除OHCI实例;
    [N]添加EHCI讲解和Chipidea EHCI实例;
    [F]修正iso传输实例;
    [F]修正usb devices信息到/sys/kernel/debug/usb/devices
    [N]介绍module_usb_driver;
    [N]介绍usb_function;
    [N]介绍usb_ep_autoconfig;
    [N]介绍usb_otg;

    [D]删除otg_transceiver;

     

    第21章 《PCI设备驱动》
    [D]整章删除

     

    第22章 《Linux设备驱动的调试》

    [F]变为第21章;

    [D]把实验室环境建设相关的节移到第3章;

    [F]修正关于gdb的set step-mode的含义讲解;

    [F]增加关于gdb的set命令的讲解;

    [F]增加gdb call命令的案例

    [D/N]删除手动编译工具链的代码,使用crosstool-ng;
    [N]更新toolchain的下载地址(codesourcery -> memtor),加入linaro下载地址;
    [N]增加pr_fmt的讲解;
    [N]增加关于ignore_loglevel bootargs的讲解;
    [N]增加EARLY_PRINTK和DEBUG_LL的讲解;

    [F]调整proc的范例,创建/proc/test_dir/test_rw;
    [N]修正关于3.10后内核proc实现框架和API的变更;
    [N]增加关于BUG_ON和WARN_ON的讲解
    [F]不再以BDI-2000为例,改为ARM DS-5;
    [N]增加关于ARM streamline性能分析器的介绍;
    [N]增加使用qemu调试linux内核的案例;
    [F]调整Oops的例子,使用globalmem,平台改为ARM;
    [F]更新LTT为LTTng。

     

    第23章 《Linux设备驱动的移植》
    [D]整章删除

     

    全新的章节

     

    第17章  《I2C、SPI、USB架构类比》

     

    本章导读

    本章类比I2C、SPI、USB的结构,从而更进一步帮助读者理解本书第12章的内容,也进一步实证Linux驱动万变不离其宗的道理。

     

    第18章  《Linux设备树(Device Tree)》

     

    本章导读

    本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP变更。

    18.1节阐明了ARM Linux为什么要采用设备树。

    18.2节详细剖析了设备树的结构、结点和属性,设备树的编译方法以及如何用设备树来描述板上的设备、设备的地址、设备的中断号、时钟等信息。

    18.3节讲解了采用设备树后,驱动和BSP的代码需要怎么改,哪些地方变了。

    18.4节补充了一些设备树相关的API定义以及用法。

    本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是步入内核3.x时代后,嵌入式Linux工程师必备的知识体系。

     

     

     

    第19章  《Linux的电源管理》

     

    本章导读

    Linux在消费电子领域的应用已经铺天盖地,而对于消费电子产品而言,省电是一个重要的议题。

    本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP变更。

    19.1节阐述了Linux电源管理的总体架构。

    19.2~19.8节分别论述了CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator、OPP以及电源管理的调试工具PowerTop。

    19.9节讲解了系统Suspend to RAM的过程以及设备驱动如何提供对Suspend to RAM的支持。

    19.10节讲解了设备驱动的Runtimesuspend。

    本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是Linux设备驱动工程师必备的知识体系。

     

     

     

    第20章 《Linux芯片级移植与底层驱动》

     

    本章导读

    本章主要讲解,在一个新的ARM SoC上,如何移植Linux。当然,本章的内容也适合MIPS、PowerPC等其他的体系架构。

    第20.1节先总体上介绍了Linux 3.x之后的内核在底层BSP上进行了哪些优化。

    第20.2节讲解了如何提供操作系统的运行节拍。

    第20.3节讲解了中断控制器驱动,以及它是如何为驱动提供标准接口的。

    第20.4节讲解多核SMP芯片的启动。

    第20.6~20.9节分别讲解了作为Linux运行底层基础设施的GPIO、pinctrl、clock和dmaengine驱动。

    本章相对《Linux设备驱动开发详解(第2版)》几乎是全新的一章内容,有助于工程师理解驱动调用的底层API的来源,以及直接有助于进行Linux的平台移植。

     

    展开全文
  • 第二章 Linux内核结构及构建 ---->这一章是自己总结的 1、内核结构(主要是下面这几个部分) 系统调用接口<—&...

    第二章 Linux 的内核结构及构建

    • ---->这一章是自己总结的

    1、内核结构(主要是下面这几个部分)

    • 系统调用接口<–>System call interface
    • 进程管理<------>Process management
    • 内存管理<------>Memory management
    • 虚拟文件系统<–>VFS
    • 网络设备<------>Net
    • 进程间通信<---->IPC
    • 设备驱动<------>Drivers
      • -------->具体对应的目录见书上59页

    2、Makefiles and build system of kernel–kbuild

    2.1 makefile包含五大部分

    • The top Makefile:内核源码根目录下的Makefile,所有内核代码编译的入口。它负责两类东西的编译:

      • vmlinux:内核映像,Image->zImage -> boot.img,img是内核的常住居民。随系统启动而运行,直到系统关机。
      • modules:内核模块 xxx.ko,这种形式,也是内核源码的一种二进制形式,从本质上来说和内核映像是一样。但是ko是以普通文件的形式存在的,不随系统启动而运行,在系统需要你的时候 insmod xxx.ko,不需要你的时候就rmmod xxx.ko.如: camera.ko,ko形式多见于外设驱动它递归进入到内核源码树子目录去构建这些目标,应该至少包含一个arch的makefile和一个arch/$(ARCH)/Makefile(跟具体硬件平台相关)
    • .config:内核源码根目录下,内核编译的配置文件,该文件直接决定哪些文件编译、怎么编译,内核配置文件包含像下面这些选项:

      • CONFIG_EXPERIMENTAL=y
      • CONFIG_BROKEN_ON_SMP=y
      • CONFIG_INIT_ENV_ARG_LIMIT=32
      • CONFIG_CROSS_COMPILE=“arm-linux-”
    • arch/$(ARCH)/Makefile:体系结构相关的Makefile,决定底层CPU相关的源码如何编译。为top Makefile提供具体硬件体系结构相关的信息。

    • scripts/Makefile.*:包含了一些通用的Makefile模式,函数,脚本等等。供其他Makefile包含

    • kbuild Makefiles_:每个子目录有一个kubild makefile,决定哪些文件编译到vmlinux(built-in),哪些编成在ko,kbuild(内核编译系统)内置了几个变量

      • obj-y: 这个变量指定的.o文件列表,最终编译到vmlinux中去
        obj-y := a.o b.o c.o
        a-y = 1.o 2.o

        • 1.o <- 1.c,2.o < 2.c
          ->a.o
        • b.o <- b.c,c.o <- c.c
          => vmlinux -> … -> boot.img
      • obj-m:obj-m这个变量指定的.o文件列表,kbuild最终会为每个.o生成一个同名的ko文件

        • obj-m := a.o b.o,a.o -> a.ko,b.o -> b.ko
      • 语法:目标名-y/目标名-objs:生成"目标名"所依赖的.o文件列表

        • obj-m := a.o b.o

          • a.c(a.s) ->a.o -> a.ko
          • b.c(b.s) ->b.o -> b.ko
        • obj-m := sb.o nb.o

        • sb-y := a.o b.o c.o

          • a.o <- a.c(a.s)
          • b.o <- b.c(b.s)
          • c.o <- c.c(c.s)
            • =>sb.o -> sb.ko
            • nb.c(nb.s) -> nb.o -> nb.ko
        • obj-$(CONFIG_XXX) += sb.o

          • kbuild只认  obj-y obj-m

          • sb.o编还是不编,得看变量CONFIG_XXX的值

          • if CONFIG_XXX = y sb.o编,而且编进vmlinux

          • if CONFIG_XXX = m sb.o编成sb.ko

          • else other , 不编

             obj-m += camera.o
             camera-objs += a.o b.o
             camera-$(CONFIG_MEIYANG) += mei.o  
            
        • obj-$(CONFIG_XXX) += xxx/

          • 表示你如果选择了CONFIG_XXX请到子目录xxx下面去读Makefile

    2.2 内核编译的两大步骤

    • 1、配置
      • make menuconfig:=> 生成一个人性化的配置菜单树(供你选择)
        • sudo apt-get install libncurses5-dev
        • 目的是生成一个 .config (配置文件,决定哪些文件编译,哪些不编译)
          • CONFIG_FOO = y
          • CONFIG_MM = m
          • CONFIG_XX = n
    • 2、编译 make(sudo ./mk -k)
      • a. 先读.config里面的内容,把相应的变量值替换到Makefile中去
        • obj-$(CONFIG_FOO) += foo.o
        • obj-$(CONFIG_MM) += mm.o
        • obj-$(CONFIG_XX) += xx.o
        • ----->
          • obj-y += foo.o
          • obj-m += mm.o
          • obj-n += xx.o
      • b. 编译
        • foo.o -> foo.o -> vmlinux
        • mm.c -> mm.o -> mm.ko
    • 3、一个例子
      • 先准备好下面的文件:my_char_drivers/hello.c,Kconfig,Makefile
      • (1) 把my_char_drivers整个目录拷贝到  内核源码目录的 drivers/char
      • (2) 修改 drivers/char/Kconfig 增加一行,让其包含my_char_drivers/Kconfig
        • vim dirvers/char/Kconfig 增加一行
        • source “drivers/char/my_char_drivers/Kconfig”
      • (3) 修改 drivers/char/Makefile 增加一行,让其包含my_char_drivers/Makefile
        • vim drivers/char/Makefile 增加-行
        • obj-$(CONFIG_zhoulong_drivers) += my_char_drivers/

    2.3 .config的内容

    • Kconfig
      • Kconfig是作为.config的配置文件
      • 内容是用kconfig特有的语言编写
      • 配置信息的数据库是一个配置选项的集合
    • Kconfig 语法
      • config
        • config <symbol>
        • <config options>
          • 关键字config开始一个新的配置入口,紧接着的一行是上一个选项的属性,举例:

             config FOO
                 tristate
                 prompt "a test"
                 depends on BAR
                 default m
                 help
                 "a example for config entry"
            
      • menuconfig
        • menuconfig <symbol>
        • <config options>
      • menu/endmenu
        • menu “some prompt info”
        • <menu options>
        • <menu block>
        • endmenu
      • if/endif
        • if <expr>
        • endif
      • source
        • source “xxx/xxx/Kconfig”
      • choice/endchoice
        • choice [symbol]
        • <choice options>
        • <choice block>
        • endchoice
          • 一个选择只能是一个布尔值或三态,布尔选项只允许单个配置选项被选中,但是三态选择还允许将任意数量的配置项设置为“m”。
          • 这个可以用在如果一个硬件有多个驱动程序存在并且只有一个驱动程序可以编译进内核,但是所有的驱动程序可以编译成模块
      • comment
        • comment “some prompt info”
        • <comment options>
          • 这定义了一个注释,它在配置过程中显示给用户,并且也与输出到输出文件。唯一可能的选项是依赖项。

    3、An example

    4、Process of making zImage(uImage)

    第三章 Linux内核及内核编程

    3.2 linux 2.6以后内核的特点(相对于linux2.4)

    1.新的调度器

    • 内核 2.6 的早期采用了 O(1) 算法,之后转移到 CFS(Completely Fair Scheduler,完全公平调度)算法。在 Linux
      3.14 中,也增加了一个新的调度类:SCHED_DEADLINE,它实现了 EDF(Earliest Deadline First,最早截止期限优先)调度算法。

    2.内核抢占

    • Linux 2.6 以后的内核版本还是存在一些不可抢占的区间,如中断上下文、软中断上下文和自旋锁锁住的区间
      在这里插入图片描述
      可以看到,在 Linux 2.4 的内核中,在 IRQ1 的中断服务程序唤醒 RT(实时)任务后,必须要等待前面一个 Normal(普通)任务的系统调用完成,返回用户空间的时候,RT 任务才能切入;而在 Linux 2.6 内核中,Normal 任务的关键部分(如自旋锁)结束的时候,RT 任务就从内核切入了。

    3.改进的线程模型

    • Linux 2.6 以后版本中的线程采用 NPTL(Native POSIX Thread Library,本地 POSIX 线程库)模型,操作速度得以极大提高,内核本身也增加了 FUTEX(Fast Userspace Mutex,快速用户态互斥体),从而减小多线程的通信开销。

    4.虚拟内存的变化

    • 从虚拟内存的角度来看,新内核融合了 r-map(反向映射)技术,显著改善虚拟内存在一定大小负载下的性能。反向映射即可以通过页结构体快速寻找到页面的映射。

    5.文件系统

    • Linux 2.6 版内核增加了对日志文件系统功能的支持,还包括对扩展属性及 POSIX 标准访问控制的支持。ext2/ext3/ext4 作为大多数 Linux 系统默认安装的文件系统,在 Linux 2.6 版内核中增加了对扩展属性的支持,可以给指定的文件在文件系统中嵌入元数据。
    • 在文件系统方面,当前的研究热点是基于 B 树的 Btrfs,Btrfs 称为是下一代 Linux 文件系统,它在扩展性、数据一致性、多设备管理和针对 SSD 的优化等方面都优于 ext4

    6.音频

    • 高级 Linux 音频体系结构(Advanced Linux Sound Architecture,ALSA),ALSA 支持 USB 音频和 MIDI 设备,并支持全双工重放等功能

    7.总线、设备和驱动模型

    • 总线是三者联系起来的基础,通过一种总线类型,将设备和驱动联系起来。总线类型中的 match() 函数用来匹配设备和驱动,当匹配操作完成之后就会执行驱动程序中的probe() 函数。

    8.电源管理

    9.联网和 IPSec

    • 改进了对 IPv6 的支持

    10. 用户界面层

    11. Linux 3.0 后 ARM 架构的变更

    • ARM Linux 的代码在时钟、DMA、pinmux、计时器刻度等诸多方面都进行了优化和调整,也删除了arch/arm/mach-xxx/include/mach 头文件目录,以至于 Linux 3.7 以后的内核可以支持多平台,即用同一份内核镜像运行于多家 SoC 公司的多个芯片,实现“一个Linux 可适用于所有的 ARM 系统”。

    3.3 Linux 内核的组成

    3.3.1 Linux 内核源代码的目录结构

    目录 功能
    arch 包含和硬件体系结构相关的代码,每种平台占一个相应的目录,如 i386、arm、arm64、powerpc、mips 等。存放的是各个平台以及各个平台的芯片对 Linux 内核进程调度、内存管理、中断等的支持,以及每个具体的 SoC 和电路板的板级支持代码。
    block 块设备驱动程序 I/O 调度
    crypto 常用加密和散列算法(如 AES、SHA 等),还有一些压缩和 CRC 校验算法
    documentation 内核各部分的通用解释和注释
    drivers 设备驱动程序,每个不同的驱动占用一个子目录,如 char、block、net、mtd、i2c 等。
    fs 所支持的各种文件系统,如 EXT、FAT、NTFS、JFFS2 等
    include 头文件,与系统相关的头文件放置在 include/linux 子目录下
    init 内核最核心的部分,包括进程调度、定时器等,而和平台相关的一部分代码放在 arch/*/kernel 目录下
    lib 库文件代码
    mm 内存管理代码,和平台相关的一部分代码放在 arch/*/mm 目录下
    net 网络相关代码,实现各种常见的网络协议
    scripts 用于配置内核的脚本文件
    security 主要是一个 SELinux 的模块
    sound ALSA、OSS 音频设备的驱动核心代码和常用设备驱动
    usr 实现用于打包和压缩的 cpio 等
    include 内核 API 级别头文件

    3.3.2 Linux 内核的组成部分

    • 进程调度
      • 在 Linux 内核中,使用 task_struct 结构体来描述进程,该结构体中包含描述该进程内存资源、文件系统资源、文件资源、tty 资源、信号处理等的指针。Linux 的线程采用轻量级进程模型来实现,在用户空间通过 pthread_create() API 创建线程的时候,本质上内核只是创建了一个新的 task_struct,并将新task_struct 的所有资源指针都指向创建它的那个 task_struct
        的资源指针。
    • 内存管理
      • 一般而言,32 位处理器的 Linux 的每个进程享有 4GB 的内存空间,0 ~ 3GB 属于用户空间,3 ~ 4GB 属于内核空间,内核空间对常规内存、I/O 设备内存以及高端内存有不同的处理方式。上次CVTE笔试考了下面这个图(画出来)
        在这里插入图片描述
      1. 虚拟文件系统
      • Linux 虚拟文件系统隐藏了各种硬件的具体细节,为所有设备提供了统一的接口。而且,它独立于各个具体的文件系统,是对各种文件系统的一个抽象。它为上层的应用程序提供了统一的 vfs_read()、vfs_write() 等接口,并调用具体底层文件系统或者设备驱动中实现的 file_operations 结构体的成员函数。
    • 4.网络接口(略)
    • 5.进程间通信(略)

    3.3.3 Linux 内核空间与用户空间

    • ARM 处理器分为 7 种工作模式:

    3.4 Linux 内核的编译及加载

    3.4.1 Linux 内核的编译

    • Linux 驱动开发者需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像。
      • 运行 make menuconfig 等时,配置工具首先分析与体系结构对应的 /arch/xxx/Kconfig 文件(xxx 即为传入的 ARCH 参数),/arch/xxx/Kconfig 文件中除本身包含一些与体系结构相关的配置项和配置菜单以外,还通过 source 语句引入了一系列 Kconfig 文件,而这些 Kconfig又可能再次通过 source 引入下一层的 Kconfig,配置工具依据 Kconfig 包含的菜单和条目即可描绘出一个如图 3.9 所示的分层结构。

    3.4.2 Kconfig 和 Makefile

    • 在 Linux 内核中增加程序需要完成以下 3 项工作
      • 将编写的源代码复制到 Linux 内核源代码的相应目录中
      • 在目录的 Kconfig 文件中增加关于新源代码对应项目的编译配置选项
      • 在目录的 Makefile 文件中增加对新源代码的编译条目

    3.4.3 Linux 内核的引导(ARM Linux 为例)

    • 。一般的 SoC 内嵌入了 bootrom,上电时 bootrom 运行。对于 CPU0 而言,bootrom 会去引导 bootloader,而其他 CPU 则判断自己是不是 CPU0,进入 WFI 的状态等待 CPU0 来唤醒它。CPU0 引导 bootloader,bootloader 引导Linux 内核,在内核启动阶段,CPU0 会发中断唤醒 CPU1,之后 CPU0 和 CPU1 都投入运行。CPU0 导致用户空间的 init 程序被调用,init 程序再派生其他进程,派生出来的进程再派生其他进程。CPU0 和 CPU1 共担这些负载,进行负载均衡。

    3.5 Linux 下的 C 编程特点

    3.5.1 Linux 编码风格

    • 在内核源代码下存在一个文件 Documentation/CodingStyle,进行了比较详细的描述

    • 在 Windows 程序中,习惯以如下方式命名宏、变量和函数

        #def ine PI 3.1415926  /* 用大写字母代表宏 */
        int minValue, maxValue; /* 变量:第一个单词全小写,其后单词的第一个字母大写 */
        void SendData(void);  /* 函数:所有单词第一个字母都大写 */
      
    • 在 Linux 中它会被命名为:

        #def ine PI 3.1415926
        int min_value, max_value;
        void send_data(void);
      

    3.6 工具链

    • 见书本page100-page101

    第4章 Linux内核模块

    4.9 模块的编译

    • 例:
    KVERS = $(shell uname -r)
    
    #Kernel module
    obj-m += hello.o
    
    #Specify flags for the module compilation
    #EXTRA_CFLAGS = -g -O0
    
    build:kernel_modules
    kernel_modules:
        make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
    clean:
        make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
    

    。。。未完

    。。。。。。。

    。。。。。。。

    展开全文
  • 1 Linux设备驱动概述及开发环境构建 1.1 设备驱动的作用 驱使硬件设备行动 1.2 无操作系统时的设备驱动 典型架构:一个无限循环中夹杂着对设备中断的检测或者对设备的轮询 1.3 有操作系统时的设备...

    1 Linux设备驱动概述及开发环境构建

    1.1 设备驱动的作用

    • 驱使硬件设备行动

    1.2 无操作系统时的设备驱动

    • 典型架构:一个无限循环中夹杂着对设备中断的检测或者对设备的轮询
      无标题.png

    1.3 有操作系统时的设备驱动

    • 并发 、内存管理

      无标题.png

    1.4 Linux 设备驱动

    1.4.1 设备的分类及特点

    ● 字符设备。
    ● 块设备。
    ● 网络设备。

    1.4.2 Linux 设备驱动与整个软硬件系统的关系

    捕获.PNG

    1.4.3 Linux 设备驱动的重点、难点

    ● 编写 Linux 设备驱动要求工程师有非常好的硬件基础,懂得 SRAM、 Flash、 SDRAM、磁盘的读写方式,UART、 I2C、 USB 等设备的接口以及轮询、中断、 DMA 的原理,PCI 总线的工作方式以及 CPU 的内存管理单元(MMU)等。
    ● 编写 Linux 设备驱动要求工程师有非常好的 C 语言基础,能灵活地运用 C 语言的结构体、指针、函数指针及内存动态申请和释放等。
    ● 编写 Linux 设备驱动要求工程师有一定的 Linux 内核基础,虽然并不要求工程师对内核各个部分有深入的研究,但至少要明白驱动与内核的接口。尤其是对于块设备、网络设备、 Flash 设备、串口设备等复杂设备,内核定义的驱动体系结构本身就非常复杂。
    ● 编写 Linux 设备驱动要求工程师有非常好的多任务并发控制和同步的基础,因为在驱动中会大量使用自旋锁、互斥、信号量、等待队列等并发与同步机制。

    2 驱动设计的硬件基础

    2.1 处理器

    2.1.1 通用处理器

    2.1.2 数字信号处理器

    捕获.PNG

    2.2 存储器

    捕获.PNG

    捕获.PNG

    2.3 接口与总线

    串口 、I2CI2C 、SPI 、USB、以太网 、PCI 和 PCI-E 、SD 和 SDIO

    捕获.PNG

    捕获.PNG

    2.4 CPLD 和 FPGA

    2.5 原理图分析

    • 符号 、网络 、描述

    2.6 硬件时序分析

    • 时序分析的意思是让芯片之间的访问满足芯片数据手册中时序图信号有效的先后顺序、采样建立时间(Setup Time)和保持时间(Hold Time)的要求

    2.7 芯片数据手册阅读方法

    2.8 仪器仪表使用

    • 万用表 、示波器 、逻辑分析仪

    3 Linux 内核及内核编程

    3.1 Linux 内核的发展与演变

    • 表 3.1 Linux 操作系统版本的历史及特点
    版 本 时 间 特 点
    Linux 0.1 1991 年 10 月 最初的原型
    Linux 1.0 1994 年 3 月 包含了 386 的官方支持,仅支持单 CPU 系统
    Linux 1.2 1995 年 3 月 第一个包含多平台(Alpha、 Sparc、 MIPS 等)支持的官方版本
    Linux 2.0 1996 年 6 月 包含很多新的平台支持,最重要的是,它是第一个支持 SMP(对称多处理器)体系的内核版本
    Linux 2.2 1999 年 1 月 极大提升 SMP 系统上 Linux 的性能,并支持更多的硬件
    Linux 2.4 2001 年 1 月 进一步提升了 SMP 系统的扩展性,同时也集成了很多用于支持桌面系统的特性: USB、 PC 卡(PCMCIA)的支持,内置的即插即用等
    Linux 2.6.0 ~ 2.6.39 2003 年 12 月~2011 年 5 月 无论是对于企业服务器还是对于嵌入式系统, Linux 2.6 都是一个巨大的进步。对高端机器来说,新特性针对的是性能改进、可扩展性、吞吐率,以及对 SMP 机器 NUMA 的支持。对于嵌入式领域,添加了新的体系结构和处理器类型。包括对那些没有硬件控制的内存管理方案的无MMU 系统的支持。同样,为了满足桌面用户群的需要,添加了一整套新的音频和多媒体驱动程序
    Linux 3.0 ~ 3.19、Linux 4.0-rc1 至今 2011 年 7 月至今 性能优化等 开发热点聚焦于虚拟化、新文件系统、 Android、新体系结构支持以及

    3.2 内核组件

    捕获.PNG

    1. 进程调度

    捕获.PNG

    2. 内存管理

    捕获.PNG

    3. 虚拟文件系统

    捕获.PNG

    4. 网络接口

    捕获.PNG

    5. 进程间通信

    • 进程间通信支持进程之间的通信, Linux 支持进程间的多种通信机制,包含信号量、共享内存、消息队列、管道、 UNIX 域套接字等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递。在实际的 Linux 应用中,人们更多地趋向于使用 UNIX 域套接字,而不是 System V IPC 中的消息队列等机制。 Android 内核则新增了 Binder 进程间通信方式。

    4 内核模块

    4.1 模块简介

    insmod ./hello.ko
    rmmod hello
    
    lsmod
    /proc/modules
    /sys/module

    4.2 模块结构

    4.2.1 加载函数

    static int __init hello_init(void)
    {
        ...
    
        return 0;
    }
    
    module_init(hello_init);

    4.2.2 卸载函数

    static void __exit hello_exit(void)
    {
        ...
    }
    
    module_exit(hello_exit);

    4.2.3 许可声明

    MODULE_AUTHOR("lin");
    MODULE_LICENSE("GPL v2");
    MODULE_DESCRIPTION("A simple param Module");
    MODULE_ALIAS("a simplest module");
    • 模块参数module_param(var, int, S_IRUGO);
    • 导出符号EXPORT_SYMBOL_GPL(func); (proc/kallsyms)

    5 文件系统与设备文件

    捕获.PNG

    捕获.PNG

    6 字符设备驱动

    6.1 驱动结构

    6.1.1 cdev结构体

    捕获.PNG

    //生成dev
    MKDEV(int major, int minor);    //major:0-19 minor:20-31
    //获取设备号
    MAJOR(dev_t dev)
    MINOR(dev_t dev)
    //cdev操作
    void cdev_init(struct cdev *, struct file_operations *);
    struct cdev* cdev_alloc(void);
    void cdev_put(struct cdev *);
    int  cdev_add(struct cdev *, dev_t, unsigned);
    void cdev_del(struct cdev *);

    6.1.2 设备号分配

    int register_chrdev_region(dev_t from, unsigned count, const char *name);
    int    alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
    
    int unregister_chrdev_region(dev_t from, unsigned count);

    6.1.3 file_operations结构体

    捕获.PNG

    7 设备驱动中的并发控制

    7.1 并发与竞态

    • 临界区:访问共享资源的代码段
    • 互斥:中断屏蔽、原子操作、自旋锁、信号量、互斥体

    7.2 编译乱序和执行乱序

    • 表 隔离指令
    指令名 功能描述
    DMB 数据存储器隔离。DMB 指令保证: 仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。
    DSB 数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访 问操作——译者注)
    ISB 指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。

    7.3 中断屏蔽

    local_irq_disable() local_irq_enable() //与自旋锁联合使用
    local_irq_save(flags) local_irq_restore(flags)
    local_bh_disable() local_bh_enable()

    7.4 原子操作

    7.4.1 整型原子操作

    • 设置

      void atomic_set(atomic_t *v, int i);
      atomic_t ATOMIC_INIT(int i);
    • 获取

      int atomic_read(atomic_t *v);
    • 加减

      void atomic_add(int i, atomic_t *v);
      void atomic_sub(int i, atomic_t *v);
      
      void atomic_inc(atomic_t *v);
      void atomic_dec(atomic_t *v);
    • 操作后测试(为0返回true,非0返回false)

      int atomic_inc_and_test(atomic_t *v);
      int atomic_dec_and_test(atomic_t *v);
      int atomic_sub_and_test(int i, atomic_t *v);
    • 操作后返回新值

      int atomic_add_return(int i, atomic_t *v);
      int atomic_sub_return(int i, atomic_t *v);
      
      int atomic_inc_return(atomic_t *v);
      int atomic_dec_return(atomic_t *v);

    7.4.2 位原子操作

    捕获.PNG

    7.5 自旋锁

    7.5.1 自旋锁

    spinlock_t lock;
    spin_lock_init(lock);
    spin_lock(lock);
    spin_trylock(lock);
    spin_unlock(lock);
    
    
    spin_lock_irq(lock); spin_unlock_irq(lock);
    spin_lock__irqsave(lock); spin_unlock_irqrestore(lock);
    spin_lock_bh(lock); spin_unlock_bh(lock);

    无标题.png

    7.5.2 读写锁

    无标题.png

    7.5.3 顺序锁

    • 读执行单元不会被写执行单元阻塞;但写执行单元进行写操作时,其他写执行单元就会自旋。

    无标题.png

    7.5.4 读-复制-更新

    • RCU: Read-Copy-Update

      捕获.PNG

      无标题.png

      7.6 信号量

      无标题.png

      7.7 互斥体

      无标题.png

      7.8 完成量

      无标题.png

    8 阻塞I/O和非阻塞I/O

    8.1 阻塞I/O和非阻塞I/O

    fd= open("/dev/ttyS1", O_RDWR | O_NONBLOCK);
    fcntl(fd, F_SETFL, O_NONBLOCK);

    8.1.1 等待队列

    //定义
    wait_queue_head_t queue_head;
    //初始化
    init_waitqueue_head(&queue_head);
    //定义及初始化
    DECLARE_WAIT_QUEUE_HEAD(name)
    //队列等待元素
    DECLARE_WAITQUEUE(name, tsk)
    //操作
    void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
    void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
    //等待事件
    wait_event(queue, condition)
    wait_event_interruptible(queue, condition)
    wait_event_timeout(queue, condition, timeout)
    wait_event_interruptible_timeout(queue, condition, timeout)
    //唤醒队列
    void wake_up(wait_queue_head_t *q);
    void wake_up_interruptible(wait_queue_head_t *q);
    //睡眠
    sleep_on(wait_queue_head_t *q);
    interruptible_sleep_on(wait_queue_head_t *q);
    static ssize_t xxx_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
    {
        ...
        DECLARE_WAITQUEUE(wait, current);
        add_wait_queue(&xxx_wait, &wait);
    
        /*等待设备缓冲区可写*/
        do {
            avail = device_writable();
            if (avail < 0) {
                if (file->f_flags & O_NONBLOCK) {
                    ret = -EAGAIN;
                    goto out;
                }
                __set_current_state(TASK_INTERRUPTIBLE);
                schedule();
                if (signal_pending(current)) {
                    ret = -ERESTARTSYS;
                    goto out;
                }
            }
        } while (avail < 0);
    
        device_write();
    out:
        remove_wait_queue(&xxx_wait, &wait);
        set_current_state(TASK_RUNNING);
    
        reutrn ret;
    }

    捕获.PNG

    8.1.2 支持等待队列的globalfifo

    无标题.png

    8.2 轮询操作

    8.2.1 轮询的概念与作用

    9.2.3 信号的释放

    1. 异步通知结构体

      struct xxx_dev{
          struct cdev cdev;
          ...
          struct fasync_struct *async_queue;
      }
      1. xxx_fasync
      static int xxx_fasync(int fd, struct file *filp, int mode)
      {
          struct xxx_dev *dev=file->private_data;
          return fasync_helper(fd, filp, mode, &dev->async_queue);
      }
      1. 释放读信号
      //xxx_write
      if(dev->async_queue)
        kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
      1. 从异步通知列表删除filp
      //xxx_release
      xxx_fasync(-1, filp, 0);

    9.4 Linux异步I/O

    9.4.1 AIO

    struct aiocb {
     
      int aio_fildes;               // File Descriptor
      int aio_lio_opcode;           // Valid only for lio_listio (r/w/nop)
      volatile void *aio_buf;       // Data Buffer
      size_t aio_nbytes;            // Number of Bytes in Data Buffer
      struct sigevent aio_sigevent; // Notification Structure
     
      /* Internal fields */
      ...
     
    };
    API 函数 说明
    aio_read int aio_read( struct aiocb *aiocbp ); 请求异步读操作
    aio_error int aio_error( struct aiocb *aiocbp ); 检查异步请求的状态
    aio_return ssize_t aio_return( struct aiocb *aiocbp ); 获得完成的异步请求的返回状态
    aio_write int aio_write( struct aiocb *aiocbp ); 请求异步写操作
    aio_suspend int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout ); 挂起调用进程,直到一个或多个异步请求已经完成(或失败)
    aio_cancel int aio_cancel( int fd, struct aiocb *aiocbp ); 取消异步 I/O 请求
    lio_listio int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig ); 发起一系列 I/O 操作

    9.4.2 内核AIO与libaio

    10 中断与时钟

    10.1 中断与定时器

    11 内存与I/O访问

    17 I2C、SPI、USB驱动架构类比

    无标题.png

    18 ARM Linux设备树

    18.1 ARM设备树起源

    • 可描述的信息:
      • CPU的数量和类别
      • 内存基地址和大小
      • 总线和桥
      • 外设连接
      • 中断控制器和中断使用情况
      • GPIO控制器和GPIO使用情况
      • 时钟控制器和时钟使用情况

    18.2 设备树的组成和结构

    18.2.1 DTS、DTC和DTB

    1. .dts:device tree source

      1.1 Soc共用部分:.dtsi (/include/ “s3c24440.dtsi”)

      1.2 模板

      /* root节点 */
      / {
          node1 {
              a-string-property = "A string";
              a-string-list-property = "first string", "second string";
              a-byte-data-property = [0x01 0x23 0x34 0x56];
              child-node1 {
                  first-child-property;
                  second-child-property = <1>;
                  a-string-property = "Hello, world";
              };
              child-node2 {
              };
          };
          node2 {
              an-empty-property;
              a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
              child-node1 {
              };
          };
      };
    2. .dtc:device tree compiler

    3. .dtb:Device Tree Blob

    展开全文
  • MongoDB实战性能优化

    2019-06-19 18:50:53
    1. 性能优化分类 mongodb性能优化分为软件层面和操作系统层面。 软件层面,一般通过修改mongodb软件配置参数来达到,这个需要非常熟悉mongodb里面的各种配置参数; 而操作系统层面,相对简单点,主要是修改操作...

    1. 性能优化分类

    mongodb性能优化分为软件层面和操作系统层面。

    软件层面,一般通过修改mongodb软件配置参数来达到,这个需要非常熟悉mongodb里面的各种配置参数;

    而操作系统层面,相对简单点,主要是修改操作系统参数,比如说:关闭传输页缓存、使用SSD替代机器硬盘等等。

    2. 软件层面优化

    2.1 设置WiredTiger的cacheSizeGB

    通过cacheSizeGB选项配置控制WiredTiger引擎使用内存的上限,默认配置在系统可用内存的60%左右。

    如果一台机器上只部署一个mongod,mongod可以使用所有可用内存,则使用默认配置即可。

    如果一台机器上部署多个mongod,或者mongod跟其他的一些进程一起部署,则需要根据分给mongod的内存配额来配置cacheSizeGB,按配额的60%左右配置即可。

    通过配置文件配置cacheSizeGB,如下:

    2.2 分配足够的Oplog空间

    Oplog是MongoDB local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。Oplog可以说是MongoDB Replication的纽带。Oplog是固定大小的,它只能保存特定数量的操作日志。如果oplog size过大,会浪费存储空间;如果oplog size过小,老的oplog记录很快就会被覆盖,那么宕机的节点很容易出现无法同步数据的现象,因此设置合理的oplog大小对mongodb很重要。MongoDB默认将其大小设置为可用disk空间的5%(默认最小为1G,最大为50G)。

    这里设置oplog为10000MB,如:

    2.3 启用Log Rotation日志切换

    防止MongoDB的log文件无限增大,占用太多磁盘空间。使用Log Rotation并及时清理历史日志文件,在配置文件配置如下红框设置。

     

    logRotate:日志回转,防止一个日志文件特别大,可选值:rename,重命名日志文件,默认值;reopen,使用Linux日志rotate特性,关闭并重新打开次日志文件,可以避免日志丢失,但是logAppend必须为true。

    timeStampFormat:指定日志格式的时间戳格式,可选值:ctime,显示时间戳Wed Dec 31 18:17:54.811;Iso8601-utc,显示时间戳以协调通用时间(UTC)在ISO-8601中的格式,例如,纽约时代的开始时间:1970-01-01t00:00: 00.000z;iso8601-local,显示当地时间ISO-8601格式显示时间戳

    2.4 设置journal日志刷新时间和flush时间

    commitIntervalMs:mongod的journal日志刷新值范围从1到500毫秒。较低的值增加了journal的耐久性,以牺牲性能为代价,在WiredTiger引擎上,默认的日志提交间隔为100毫秒,增大commitIntervalMs可以降低磁盘的IO压力,起到一定的优化作用。不过一般情况下,不建议修改

    syncPeriodSecs:mongod使用fsync操作将数据flush到磁盘的时间间隔,默认值为60(单位:秒),增大该值也可以降低磁盘IO压力,起到一定优化作用。一般情况下,强烈建议不要修改此值。mongod将变更的数据写入journal后再写入内存,并间歇性的将内存数据flush到磁盘中,即延迟写入磁盘,有效提升磁盘效率。此指令不影响journal存储,仅对mongod有效。

    3. 系统层面优化

    3.1 MongoDB连接内存优化

    MongoDB服务器内存要满足connection overhead + data size + index size,即连接数开销 + 热点数据 + 索引。

    linux操作系统默认每个连接数占用10M内存。

    使用ulimit -a 查看stack size,即为每个连接数占用的内存。

     

     所有连接数消耗的内存加起来相当惊人,推荐把Stack设置小一点,比如说1024:在linux命令窗口输入ulimit -s 1024。这种方式的缺点是,重新打开一个shell命令窗口就失效啦,需要重新执行这一条命令。

    永久生效的方式,修改/etc/profile,在最后添加ulimit -s 1024,然后保存并source /etc/profile

    3.2 MongoDB连接数优化

    MongoDB连接数主要是通过提高操作系统的默认文件描述符和进程/线程数限制Linux默认的文件描述符数和最大进程数对于MongoDB来说一般会太低。建议把这个数值设为100000以上。因为MongoDB服务器对每一个数据库文件以及每一个客户端连接都需要用到一个文件描述符。如果这个数字太小的话在大规模并发操作情况下可能会出错或无法响应。报以下错误信息:

    "too many open files"
    "too many open connections"

    上面报错的信息,说明打开的连接数太多了,有两个限制mongod/mongos连接数的地方:

    ① 操作系统的ulimit限制,本节重点介绍;

    ② mongodb自身的限制;

    操作系统的ulimit限制,可以用ulimit -a查看open files,是否够大。linux下默认的open files是1024,在提供服务的时候往往太小。

    可以通过以下命令来修改这些值,暂时性,重新打开shell命令窗口会失效:

    ulimit -n 1048576
    ulimit -u 524288

    这时可以通过修改/etc/security/limits.conf 持久化设置允许用户/进程打开文件句柄数,这一步需要重启系统,不然不起作用,在limits.conf添加如下设置:

    * soft nofile 1048576
    * hard nofile 1048576
    * soft nproc 524288
    * hard nproc 524288

    3.3 关闭Transparent Huge Pages

    Transparent Huge Pages(THP)是Linux的一种内存管理优化手段,通过使用更大的内存页来减少Translation Lookaside Buffer(TLB)的额外开销。MongoDB数据库大部分是比较分散的小量数据读写,THP对MongoDB这种情况会有负面的影响,所以建议关闭。

    暂时性的做法,会失效:

    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo never > /sys/kernel/mm/transparent_hugepage/defrag

    永久性做法,修改/etc/rc.d/rc.local,添加如下设置:

    if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
       echo never > /sys/kernel/mm/transparent_hugepage/enabled
    fi
    if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
       echo never > /sys/kernel/mm/transparent_hugepage/defrag
    fi

    3.4 使用XFS文件系统,同时禁掉数据库文件的atime

    MongoDB在WiredTiger存储引擎下建议使用XFS文件系统。Ext4最为常见,但是由于ext文件系统的内部journal和WiredTiger有所冲突,所以在IO压力较大情况下表现不佳。

     在使用XFS文件系统的同时,我们建议在文件系统的mount参数上加上noatime,nodiratime两个选项。用noatime mount的话,文件系统在程序访问对应的文件或者文件夹时,不会更行对应的access time。

    一般来说,Linux会给文件记录三个时间,change time,modify time 和 access time。我们可以通过stat来查看文件的三个时间。其中access time指文件最后一次被读取的时间,modify time指的是文件的文本内容最后发生变化的时间,change time指的是文件的inode最后发生变化(比如位置、用户属性、组属性等)的时间。

    一般来说,文件都是读多写少的,而且我们也很少关心某一文件最近什么时间被访问了,所以,我们建议采用noatime选项,这样文件系统不记录access time,避免浪费资源。禁止系统对文件的访问时间更新会有效提高文件读取的性能。

     操作如下:

    1) Centos7的安装xfsprogs-4.5.0-15.el7.x86_64.rpm包,而Centos6的安装xfsprogs-3.1.1-20.el6.x86_64.rpm。

         rpm -ivh xfsprogs-3.1.1-20.el6.x86_64.rpm

    2) 格式化为XFS文件系统,有两种情况,一是把已存在的Ext4等文件系统格式化为XFS文件系统,二是把尚未格式化的磁盘格式化为XFS文件系统。

     一、把已存在的Ext4格式化为XFS文件系统,以/opt目录为例,我们把/opt作为mongodb的安装及存储目录:

    ①  首先使用命令 lsblk -f 查看磁盘分区及其对应的目录

          

    ②  然后 umount /opt 把/opt目录卸载掉

         

    ③  使用 mkfs.xfs -f /dev/sda5 把ext4文件系统覆盖为xfs文件系统,并使用lsblk -f查看是否修改成功

          

    ④  接着修改/etc/fstab文件,让系统开机自动挂载该磁盘分区为/opt,在/etc/fstab文件里,把/opt对应那行的ext4改为xfs,并在defaults后面加上“,noatime”,把atime禁掉

          

    ⑤  最后,把/opt挂载上,并禁掉atime,并用 lsblk -f 查看

          

    二、把尚未格式化的磁盘格式化为XFS文件系统

    ①  首先使用命令 fdisk -l 查看空闲的磁盘分区

          

    从上图可以看出磁盘/dev/sda总大小为42.9GB,/dev/mapper/centos-root占用29GB,而/dev/mapper/centos-swap占用2GB,故空余尚未格式化的磁盘大约占10GB;磁盘/dev/sda占用83886080个sectors,而目前/dev/sda2使用到的sectors为62914559,还剩余20971521个sectors尚未使用,这也是看出尚未格式化的磁盘的方法;

    ②  创建新的磁盘分区

          

    ③ 执行partprobe或者重启

    执行 partprobe 命令用于将磁盘分区表变化信息通知内核,并请求操作系统重新加载分区表,可以避免必须重新启动的问题

    执行 partprobe 命令之前,使用 lsblk -f 来查看磁盘分区,没有看到/dev/sda3;执行 partprobe 命令后,再次使用 lsblk -f 查看磁盘分区

    后面的操作同方式一的④⑤操作,挂载并禁掉atime,最后在/etc/fstab上面添加/dev/sda3及其对应挂载的路径

    3.5 使用SSD或RAID10来提供存储IOPS能力

    MongoDB是一个高性能高并发的数据库,其大部分的IO操作为随机更新。一般来说本机自带的SSD是最佳的存储方案。如果使用普通的磁盘,建议使用RAID10条带化来提供IO通道的并发能力。

    注意:

    IOPS (Input/Ouput Operations Per Second):是存储设备每秒读写的次数。

    RAID10:RAID1 + RAID0的结合,RAID1是一个冗余的备份阵列,RAID0是负责数据的读写阵列。例如:磁盘1和磁盘2组成一个Raid1,磁盘3和磁盘4又组成另外一个Raid1;这两个Raid1组成了一个新的Raid0。其中磁盘1和磁盘2的数据是相互备份的,磁盘3和磁盘4的数据是相互备份的。

    3.6 为Data和Journal/log分别使用单独的物理卷

    MongoDB很多的性能瓶颈和IO相关。建议为日志盘(Journal和MongoDB系统日志)单独设定一个物理卷,减少对数据盘IO的资源占用。

    MongoDB系统日志可以直接在命令行或者配置文件参数内指定。Journal日志不支持直接指定到另外的目录,可以通过对Journal目录创建symbol link的方式来解决。

    如,/resource目录和 /opt目录分别是两个不同的物理磁盘,

    data存储数据设置在 /resource 目录,通过如下配置参数设置

    Journal和log数据设置在 /opt 目录,通过如下配置参数设置

     Journal日志是MongoDB的预读写日志,用于数据库崩溃时的数据恢复,可以在配置文件配置参数禁掉,通常生产环境不建议禁掉。Journal一般是在data存储目录下,通过创建软连接symbol link来把路径引到/opt目录下

    ln -s /resource/mongodbCluster/shard1/data/journal  /opt/mongodbCluster/shard1/journal

     

    3.7 禁用NUMA

    非一致存储访问结构(NUMA:Non-Uniform Memory Access)是最新的内存管理技术。在一个使用NUMA技术的多处理器Linux系统上,当内存不足时,会采用SWAP的方式来获得内存,而SWAP会导致数据库性能急剧下降,所以应该禁止NUMA的使用。另外,MongoDB在NUMA环境下运行性能有时候可能会变慢,特别是在进程负载很高的情况下。

    禁用NUMA的方法,在mongod前面加“numactl --interleave=all”:

    numactl --interleave=all  /opt/app/mongodb/bin/mongod -f /opt/app/mongodbCluster/conf/config.conf

    3.8 设置vm.swappiness

    vm.swappiness是操作系统控制物理内存交换出去的策略。它允许的值是一个百分比的值,最小为0,最大为100,该值默认为60.

    vm.swappiness设置为0表示尽量少swap,100表示尽量将inactive的内存页交换出去。

    具体的说,当内存基本用满的时候,系统会根据这个参数来判断是把内存中很少用到的inactive内存交换出去,还是释放数据的cache。cache中缓存着从磁盘读出来的数据,根据程序的局部性原理,这些数据有可能在接下来又要被读取;inactive内存顾名思义,就是那些被应用程序映射着,但是“长时间”不用的内存。

    我们可以利用vmstat看到inactive的内存数量,也可以通过 /proc/meminfo 看到更详细的信息:

     

    MonogDB本身也是一个内存使用量较大的数据库,它占用的内存比较多,不经常访问的内存也会不少,这些内存如果被linux错误的交换出去了,将浪费很多CPU和IO资源,极大地降低数据库的性能。如果vm.swappiness设置为0,会带来内存溢出的问题,当内存不足时,会强制把某些程序kill掉。所以最好在MongoDB的服务器上设置vm.swappiness的值在1~10之间,尽可能少地使用swap。如果服务器内存比较大时,可以考虑设置vm.swappiness=0。

    3.9 修改磁盘调度算法

    对于磁盘I/O,Linux提供了Completely Fair Queuing(CFQ),Deadline scheduler和NOOP三种调度策略

    目前Linux默认调度策略是CFQ,它声称对每一个IO请求都是公平的,这种调度策略对大部分应用都是适用的。但是如果数据库有两个请求,一个请求3次IO,一个请求10000次IO,由于绝对公平,3次IO的请求需要跟10000次IO的请求竞争,可能要等待上千个IO完成才能返回,导致它的响应时间非常慢。并且如果在处理的过程中,又有很多IO请求陆续发送过来,部分IO请求甚至可能一直无法得到调度被“饿死”。而Deadline scheduler调度策略则可以兼顾到一个请求不会在队列中等待太久导致饿死,对数据库这种应用来说更加适用。

    暂时性设置,会失效:

    echo deadline > /sys/block/sda/queue/scheduler

    永久性设置,对于Centos6来说,在/etc/grub.conf的kernel行最后添加elevator=deadline来永久生效:

    对于Centos7来说,执行如下命令:

    grubby --update-kernel=ALL --args="elevator=deadline"

    然后重启生效。

    注意:

    一般Centos7默认支持的是deadline算法,而Centos6默认支持的cfq,而一般我们会在SSD硬盘环境中使用noop算法。

    3.10 预读值(readahead)设置

    预读值是文件操作系统的一个优化手段。Linux预读默认256个扇区,大小为128K。MongoDB很多都是随机访问,对于随机访问,预读值readhead应该设置较小为好。采用Linux的blockdev命令来把预读readhead设置小一点,可以减少内存中无用数据占用,从而优化IO性能。RA代表预读大小(扇区),推荐数值是16到32,如文档较小,预读数值可以小一点,修改后MongoDB重启才能生效。

    可以使用下述命令来显示当前系统的预读值:

    暂时性更改预读值,执行命令 blockdev --setra 32 /dev/sdb ,/dev/sdb为mongodb存储的磁盘设备:

    永久性更改,将 blockdev --setra 32 /dev/sdb 写入配置文件/etc/rc.local,否则重启就会失效:

    3.11 设置Linux内核参数

    打开/etc/sysctl.conf,在里面添加如下内容:

    #并发连接backlog设置
    net.core.somaxconn=32768
    net.core.netdev_max_backlog=16384
    
    net.core.rmem_default=262144
    net.core.wmem_default=262144
    net.core.rmem_max=16777216
    net.core.wmem_max=16777216
    net.core.optmem_max=16777216
    
    #可用知名端口范围:
    net.ipv4.ip_local_port_range='1024 65535'
    net.ipv4.tcp_max_syn_backlog=16384
    
    #TCP Socket 读写 Buffer 设置:
    net.ipv4.tcp_rmem='1024 4096 16777216'
    net.ipv4.tcp_wmem='1024 4096 16777216'

    添加完之后,不重启更新:sysctl -p

    4. 总结

    参考资料:

     http://www.ywnds.com/?p=8656

     https://blog.csdn.net/liumiaocn/article/details/78877957

     https://www.cnblogs.com/kerrycode/p/4743015.html

     https://blog.csdn.net/yisun123456/article/details/78593909?utm_source=blogxgwz4

     https://blog.csdn.net/yisun123456/article/details/78593832

     http://www.mongoing.com/archives/8781

     https://www.cnblogs.com/Joans/p/7723554.html

     http://www.ywnds.com/?p=6502

    展开全文
  • 7.1 并发与竞态 并发是指多个执行单元同时、并发的被执行,而并发的执行单元对共享资源(硬件资源、软件上的的全局变量、静态变量等)的访问则很容易导致竟态 竟态发生在以下几种情况 对称多处理器(SMP)的多个CPU...

    7.1 并发与竞态

    • 并发是指多个执行单元同时、并发的被执行,而并发的执行单元对共享资源(硬件资源、软件上的的全局变量、静态变量等)的访问则很容易导致竟态
    • 竟态发生在以下几种情况
      • 对称多处理器(SMP)的多个CPU
      • 单CPU内进程与抢占它的进程
      • 中断与进程
    • 解决方法是保证对共享资源的互斥访问
      • 访问共享资源的代码区域称为临界区,临界区需要以某种互斥机制保护
        • 互斥途径:中断屏蔽、原子操作、信号量、自旋锁、互斥体等

    7.2 编译乱序和执行乱序

    • 编译乱序是编译器的问题,而执行乱序是处理器运行时的行为
    • 编译屏障:在代码中设置barrier()屏障来阻挡编译器优化
    • 内存屏障指令:解决多核间一个核的内存行为对另一个核可见的问题
      • arm处理器的内存屏障指令有
        • DMB:数据内存屏障
        • DSB:数据同步屏障
        • LSB:指令同步屏障
      • linux的自旋锁、互斥量的等互斥逻辑需要用到上述指令

    7.3 中断屏蔽

    • 中断屏蔽的使用方法
      • local_irp_disable()与local_irp_enable()只能禁止或是能本CPU内的中断
      • local_irp_save(flags)与local_irp_restore(flags):处理屏蔽或使能中断还能保存目前CPU的中断信息,对arm就是保存和恢复cpsr
    • local_bh_disable()和loacal_bh_diable():禁止和使能中断的底半部
    local_irp_disable();
    ...
    critical section /*临界区*/
    ...
    local_irp_enable();

    7.4 原子操作

    • 原子操作可以保证对一个整型数据的修改是排他性的
    • 原子操作函数分为对整型和位的原子操作

    7.4.1 对整型的原子操作

    • 设置原子变量的值
    //设置原子变量的值为i
    void atomic_set(atomic_t *v, int i);
    //定义原子变量v并初始化为0
    atomic_t v = ATOMIC_INIT(0);
    • 获取原子变量的值
    //返回原子变量的值
    atomic_read(atomic_t *v);
    • 原子变量加、减
    //原子变量增加1
    void atomic_sub(int i, atomic_t *v);
    • 原子变量自增、自减
    //原子变量增加1
    void atomic_inc(atomic_t *v);
    //原子变量减少1
    void atomic_dec(atomic_t *v);
    • 操作并测试,下属操作对原子变量执行自增、自检和减操作后(没有加操作)。测试其是否为0,位0返回true,否则返回false
    int atomic_inc_and_test(atomic_t *v);
    int atomic_dec_and_test(atomic_t *v);
    int atomic_sub_and_test(int i, atomic_t *v);
    • 操作并返回,丅述操作是对原子变量的加、减和自增、自减操作,并返回新的值
    int atomic_add_return(int i, atomic_t *v);
    int atomic_sub_return(int i, atomic_t *v);
    int atomic_intc_return(atomic_t *v);
    int atomic_dec_return(atomic_t *v);

    7.4.2 位原子操作

    • 设置位,丅述设置addr地址的底nr位
    void set_bit(nr,void *addr);
    • 清除位
    void change_bit(nr, void *addr);
    • 改变位,下述代码对addr的nr位进行反置
    void change_bit(nr, void *addr);
    • 测试位,返回addr的第nr位
    test_bit(nr, void *addr)
    • 测试并操作位
    int test_and_se_bit(nr, void *addr);
    int test_and_clear_bit(nr, void *addr);
    int test_and_change_bit(nr, void *addr);
    • 例如:使用原子变量使得文字只能被一个进程打开
    static atomic_t xxx_available = ATOMIC_INIT(1);
    
    static int xxx_open(struct inode *inode, struct file *filp)
    {
        ...
        if(!atomic_dev_and_test(&xxx_available)){
            atomic_inc(&xxx_available);
            return -EBUSY;//已打开
        }
        ...
        return 0;//成功
    }
    
    static int xxx_release(struct inode* inode, struct file *filp){
            atomic_inc(&xxx_available);//释放设备
            return 0;
        }

    7.5 自旋锁

    7.5.1 自旋锁的使用

    • 自旋锁是一种典型的对临界资源进行互斥访问的手段,其名称来源于他的工作方式
    • 为了获取自旋锁,在某CPU上的运行需要先执行一个原子操作,该操作测试并设置某个内存变量。
      • 如果测试结果表明锁已经空闲,则程序成功获取自旋锁并继续执行
      • 如果测试结果表明锁仍在被使用,则程序将在一个小的循环内重复这个“测试并设置”操作,即所谓的自旋
    • linux系统中对自旋锁的相关操作
      • 定义自旋锁:spinlock_t lock;
      • 初始化自旋锁:spin_lock_init(lock);
      • 获得自旋锁:spin_lock(lock);或者spin_locktry(lock);
      • 释放自旋锁:spin_unlock(lock);
    • 自旋锁主要针对SMP或者单CPU单内核可抢占的情况
    • 对于单CPU但是内核不支持抢占的情况,自旋锁自动转化为空操作
      • 单CPU系统和可抢占的系统中,自旋锁持有期间中内核的抢占将被禁止
      • 多核SMP的情况下,任何一个核拿到了自旋锁,该核上的抢占调度也暂时禁止,但是没有禁止另外一个核的抢占调度
    • 自旋锁的临界区可以被中断或者底半部影响,因而需要自旋锁和其他操作的配合
      • spin_lock_irp() = spin_lock() + local_irq_disable()
      • spin_unlock_irp = spin_unlock() +local_irq_enable();
      • spin_lock_irqsave() = spin_lock() + local_irq_save();
      • spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
      • spin_lock_bh() = spin_lock() + local_bh_disable()
      • spin_unlock_bh() = spin_unlock() + local_bh_enable()
    • 使用自旋锁应该谨慎,同时需要注意几个问题
      • 自旋锁是忙等待,在自旋的时候CPU不做其他的操作,因此只有在占用锁很短的情况下才使用自旋锁
      • 可能因为递归的使用而使系统死锁
      • 在自旋期间,不能调用调用可能引起进程调度的函数,可能导致内核崩溃
      • 在单核情况下,也该认为自己的CPU是多核的,以突出驱动的跨平台性

    7.5.2 读写自旋锁

    • 自旋锁不关心临界区在进行什么操作,一视同仁
    • 自旋锁的衍生锁读写自旋锁允许读的并发,它保留了自选的概念,在写方面只能有一个进程,读方面可以有多个执行单元,但是读和写不能同时进行
    • 定义并初始化读写自旋锁:
    rwlock_t my_rwlock1 = RW_LOCK_UNLOCKED; //静态初始化
    rwlock_t my_rwlock2;
    rwlock_init(&my_rwlock2); //动态初始化
    • 读锁定:
    void read_lock(rwlock_t* lock);
    void read_lock_irqsave(rwlock_t* lock, unsigned long flags);
    void read_lock_irq(rwlock_t* lock);
    void read_lock_bh(rwlock_t* lock);
    • 读解锁:
    void read_unlock(rwlock_t* lock);
    void read_unlock_irqrestore(rwlock_t* lock, unsigned long flags);
    void read_unlock_irq(rwlock_t* lock);
    void read_unlock_bh(rwlock_t* lock);

    在对共享资源进行读取之前,应该先调用读锁定函数锁定共享资源,完成之后再调用读解锁函数释放共享资源;

    • 写锁定:
    void write_lock(rwlock_t* lock);
    void write_lock_irqsave(rwlock_t* lock, unsigned long flags);
    void write_lock_irq(rwlock_t* lock);
    void write_lock_bh(rwlock_t* lock);
    void write_trylock(rwlock_t* lock);
    • 写解锁:
    void write_unlock(rwlock_t* lock);
    void write_unlock_irqrestore(rwlock_t* lock);
    void write_unlock_irq(rwlock_t* lock);
    void write_unlock_bh(rwlock_t* lock);
     - 在对共享资源进行写操作之前,应该先调用写锁定函数锁定共享资源,完成之后再调用写解锁函数释放共享资源;与spin_trylock()一样,write_trylock()也只是尝试获得写自旋锁,不管是否成功,都会立即返回;
    
    • 读写自旋锁使用套路:
    rwlock_t lock;      //定义读写自旋锁
    rwlock_init(&lock); //初始化读写自旋锁
    
    read_lock(&lock);   //读时加锁
    ......
    //临界区操作
    ......
    read_unlock(&lock); //读后解锁;
    
    write_lock_irqsave(&lock, flags);    //写时加锁
    ......
    //临界区操作
    ......
    write_lock_irqrestore(&lock, flags); //写后解锁;

    7.5.3 顺序锁

    • 顺序锁相关操作:
    void write_seqlock(seqlock_t *s1);
    void write_sequnlock(seqlock_t *s1);
    
    //宏调用,相当于:write_seqlock()+local_irq_save()
    write_seqlock_irqsave(lock,flags)
    write_sequnlock_irqrestore(lock,flags)
    
    //宏调用,相当于:write_seqlock()+local_irq+disable()
    write_seqlock_irq(lock)
    write_sequnlock_irq(lock)
    
    //宏调用,相当于:write_seqlock()+local_bh_disable()
    write_seqlock_bh(lock)
    write_sequnlock_bh(lock)
    
    int write_tryseqlock(seqlock_t *s1),此函数和上面提到的类似。
    • 写执行单元使用如下一种模式的顺序锁:
    write_seqlock(&seqlock);
    .........//写操作代码块
    write_sequnlock(&seqlock);
    • 读执行单元涉及如下顺序锁操作:
    读开始:unsigned read_seqbegin(const seqlock_t *s1);
    //读执行单元在访问共享资源时要,调用该函数,返回锁s1的顺序号
    read_seqbegin_irqsave(lock,flags)  
    //等同于:local_irq_save()+read_seqbegin()
    重读:int read_seqretry(const seqlock_t *s1,unsigned iv) 
    //在读结束后调用此函数来检查,是否有写执行单元对资源进行操作,若有,则重新读。iv 为锁的顺序号。

    7.5.4 读-复制-更新(RCU)

    • RCU的读端没有锁、内存屏蔽、原子指令类的开销,机会可以认为是直接读
    • RCU的写端访问它的共享资源前首先要复制一个副本,然后对副本进行修改,然后使用一个回调机制在适当的时机把指向原来数据的指针重新指向新的修改数据
      • 这个时机就是所有引用该数据的CPU都退出对共享数据读操作的时候
      • 等待适当时机的这一时期称为宽限期
    • RCU的有点
      • 允许多个读执行单元同时访问保护区
      • 允许多个读写执行单元同时访问保护区数据
    • RCU不能替代读写锁
      • 因为RCU的写执行单元的同步开销较大,当写较多的时候,对读执行单元的性能提高不能弥补写执行单元同步导致的损失
    • 参考:

    7.6 信号量

    • 定义信号量
    struct semaphore sem;
    • 初始化:
    void sema_init (struct semaphore *sem, int val);
    void init_MUTEX (struct semaphore *sem); //将sem的值置为1,表示资源空闲
    void init_MUTEX_LOCKED (struct semaphore *sem); //将sem的值置为0,表示资源忙
    • 申请内核信号量所保护的资源:
    //可引起睡眠
    void down(struct semaphore * sem); 
    //down_interruptible能被信号打断
    int down_interruptible(struct semaphore * sem);  
    // 非阻塞函数,不会睡眠。无法锁定资源则马上返回
    int down_trylock(struct semaphore * sem); 
    • 释放内核信号量所保护的资源:
    void up(struct semaphore * sem);

    7.7 互斥体

    • 互斥体提供了两种机制:经典互斥体和实时互斥体
    • 经典互斥体结构体: (会导致无限制优先级反转问题)
    struct mutex {
        /* 1: unlocked, 0: locked, negative: locked, possible waiters */
        atomic_t  count;
        spinlock_t
        wait_lock;
        struct list_head
        wait_list;
        #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
        struct task_struct
        *owner;
        #endif
        #ifdef CONFIG_DEBUG_MUTEXES
        const char 
        *name;
        void  *magic;
        #endif
        #ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map
        dep_map;
        #endif
    };
    
    • 实时互斥体结构体:
    struct rt_mutex {
        raw_spinlock_t
        wait_lock;
        struct plist_head
        wait_list;
        struct task_struct
        *owner;
        #ifdef CONFIG_DEBUG_RT_MUTEXES
        int  save_state;
        const char 
        *name, *file;
        int  line;
        void  *magic;
        #endif
    };
    • 操作:
    struct mutex my_mutex;    
    mutex_init(&my_mutex);   
    void mutex_lock(struct mutex* lock);                       //获取互斥体,不可被信号中断
    void mutex_lock_interruptible(struct mutex* lock);     //获取互斥体,可被信号打断
    int mutex_trylock(struct mutex* lock);                     //尝试获取互斥体
    void mutex_unlock(struct mutex* lock);                     //释放互斥体
    
    int mutex_is_locked(struct mutex* lock):
    //该函数检查互斥锁lock是否处于锁定状态。返回1,表示已锁定;返回0,表示未锁定;
    int mutex_lock_interruptible(struct mutex* lock);  //该函数可被信号打断
    int mutex_lock_killable(struct mutex* lock);       //该函数可被kill信号打断
    
    • 用例:
    struct mutex my_mutex;   
    mutex_init(&my_mutex);   
    
    mutex_lock(&my_mutex);  
    ...临界区...                 
    mutex_unlock(&my_mutex); 

    7.8 完成量

    • 完成量:表示一个执行单元需要等待另一个执行单元完成某事后方可执行。
      • 它是一种轻量级机制,为了完成进程间的同步而设计
      • 使用完成量等待时,调用进程是以独占睡眠方式进行等待的
      • 不是忙等待
    • 结构体
      • done变量是完成量要保护的对象
      • wait则是申请完成量的进程等待队列
    struct completion {
        unsigned int done;
        wait_queue_head_t wait;
    };
    • 初始化函数
      • done变量被初始化为0.
      • 内核代码中wait_for_common函数其实就是对done变量作判断,若done变量没有大于0,则它一直处于while循环中。
      • complete函数就是对done变量加1。wait_for_common函数便会退出while循环,同时将done减1,表示申请完成量成功。
    static inline void init_completion(struct completion *x)
    {
    x->done = 0;
    init_waitqueue_head(&x->wait);
    }
    • 操作:
    struct completion my_completion;    //定义完成量my_completion
    
    init_completion(&my_completion);    //初始化完成量my_completion
    
    void wait_for_completion(struct completion* comp)
    //该函数等待一个完成量被唤醒。该函数会阻塞调用进程,如果所等待的完成量没有被唤醒,那就一直阻塞下去,而且不会被信号打断;
    
    int wait_for_completion_interruptible(struct completion* comp)
    //该函数等待一个完成量被唤醒。但是它可以被外部信号打断;
    
    int wait_for_completion_killable(struct completion* comp)
    //该函数等待一个完成量被唤醒。但是它可以被kill信号打断;
    
    unsigned long wait_for_completion_timeout(struct completion* comp, unsigned long timeout)
    //该函数等待一个完成量被唤醒。该函数会阻塞调用进程,如果所等待的完成量没有被唤醒,调用进程也不会一直阻塞下去,而是等待一个指定的超时时间timeout,当超时时间到达时,如果所等待的完成量仍然没有被唤醒,那就返回;超时时间timeout以系统的时钟滴答次数jiffies计算
    
    bool try_wait_for_completion(struct completion* comp)
    //该函数尝试等待一个完成量被唤醒。不管所等待的完成量是否被唤醒,该函数都会立即返回
    
    bool completion_done(struct completion* comp)
    //该函数用于检查是否有执行单元阻塞在完成量comp上(是否已经完成),返回0,表示有执行单元被完成量comp阻塞;相当于wait_for_completion_timeout()中的timeout=0
    
    void complete(struct completion* comp)
    //该函数只唤醒一个正在等待完成量comp的执行单元
    
    void complete_all(struct completion* comp)
    //该函数唤醒所有正在等待同一个完成量comp的执行单元
    
    NORET_TYPE void complete_and_exit(struct completion* comp, long code)
    //该函数唤醒一个正在等待完成量comp的执行单元,并退出,code为退出码
    • 注意:在内核处理完请求之后,必须调用这三个函数中的一个,来唤醒其它正在等待的进程
    展开全文
  • NFS是网络文件系统(Network File System)的简称,是分布式计算系统的一个组成部分,可实现在异种网络上共享和装配远程文件系统。NFS由Sun公司开发,目前...NFS文件服务器是Linux最常见网络的服务之一。尽管它的规则简
  • 由于数据包的转发一般是具备路由功能的设备所关注,在本文中没有叙述,(在 Linux 内核中,分别使用了基于哈希的路由查找和基于动态 Trie 的路由查找算法)。本文集中于发送路径和接收路径上的优化方法分析,其中的 ...
  • GitHub Linux内核调试的方式以及工具集锦 LDD-LinuxDeviceDrivers/study/debug 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 因本人技术水平和知识面...
  • 《嵌入式Linux性能详解》,史子旺 https://perf.wiki.kernel.org/index.php/Main_Page,perf主页 http://www.docin.com/p-619608212.html,Linux 的系统级性能剖析工具-perf (一) - 淘宝内核...
  • 内核裁剪详细说明

    2020-02-07 17:22:10
    第一部分Linux内核裁减 (1)安装新内核: i)将新内核copy到/usr/src下并解压: #tar -zxvf linux-2.6.38.4.tar.gz ii) 将名为linux的符号链接删掉,这是旧版本内核的符号链接. #ln -s linux-2.6.38.4 ...
  • 一、内核定时器 1.基本概念 在某些场景下,我们需要在特定的时间后做某些动作,但是又不想一直等待而浪费CPU,这个时候定时器是非常合适的机制。定时器用于在将来的某个时间点执行某个函数以完成特定的任务。 内核...
  • 本文题目有点大,但其实我只想描述一些我个人一直比较关注的特性,并且不会太详细,跟往常一样,主要是帮忙理清思路的,不会分析源码。这主要是为了哪一天突然忘了的时候,一目十行扫一眼就能记忆当时的理解,不然写...
  • MySQL对于很多Linux从业者而言,是一个非常棘手的问题,多数情况都是因为对数据库出现问题的情 况和处理思路不清晰。在进行MySQL的优化之前必须要了解的就是MySQL的查询过程,很多的查询优 化工作实际上就是遵循一些...
  • Linux内核之中断初探

    2015-12-17 22:16:02
    最早的时候,是使用轮询机制,但是这种机制很浪费资源的,会降低计算机的整体性能。所以诞生了一种更好的机制,就是让硬件在需要的时候再向内核发出信号。这就是中断机制。 中断 中断是硬件发出的,它给处理器发送...
  • 在 Android开发中,性能优化策略十分重要 本文主要讲解性能优化中的内存优化,希望你们会喜欢 目录 1. 定义 优化处理 应用程序的内存使用、空间占用 2. 作用 避免因不正确使用内存 &amp; ...
  • Linux内核维护者Greg Kroah-Hartman和Ben Hutchings发布了新版本的Linux 4.14,4.9,4.3,3.16,3.18和3.12 LTS(长期支持),这些内核系列显然修补了影响大多数现代处理器的两个关键安全缺陷之一。 Linux 4.14.11,...
  • Linux内核参数优化

    2013-02-05 15:13:44
    Linux内核参数优化 转自: http://www.linuxsee.com/linux%E6%9D%82%E8%AE%B0/linux%E5%86%85%E6%A0%B8%E5%8F%82%E6%95%B0%E4%BC%98%E5%8C%96.html 我的内核心参数调整原则是,哪个遇到瓶颈调哪个,谨慎...
  • Linux配置并编译内核

    2018-08-03 16:31:09
     配置内核代码并不会花费太长时间。配置工具会询问许多问题并且允许开发者配置内核的每个方面。如果你有不确定的问题或者特性,你最好使用配置工具提供的默认值。本系列教程会使读者逐步了解配置内核的整个过程。 ...
  • Linux之TCPIP内核参数优化 /proc/sys/net目录  所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),例如下面这些重要的参数: ...
1 2 3 4 5 ... 20
收藏数 5,992
精华内容 2,396