技术交流

好好学习,天天向上。

0%

PCI介绍

PCI总线有自己的I/O空间(32位)和内存空间(32/64位),CPU访问PCI空间需要映射,硬上电自动完成将不同的外设映射到不同的CPU地址空间上。PCI设备的使用模型是这样的,外设可以抽象成一片内存或者一排连续的端口,具体而言每个功能256字节的配置内存。它们可以被排布在PCI总线地址空间上,然后映射到host CPU的地址空间里。每个PCI插槽有4个中断引脚,每个功能可以使用其中一个。每个PCI外设由domain(16位)、bus(8位)、device(5位)以及function(3位)组成的编号来标识。每个domain可以有最多256个bus,每个bus上最多可以有32个device,每个divece上最多可以有8中function。查看当前环境PCI信息可以用下面几种命令

1
2
3
lspci
cat /proc/pci
cat /proc/bus/pci

PCI设备暴露出的256字节的配置内存,前64字节是标准化的,PCI设备始终使用小端字节序。通常来说,在这256个配置寄存器中由几个只读寄存器,由厂商设定,用于识别设备

名称 描述 是否常用
vendorID 厂商标识信息,生产PCI设备的厂商需要注册
deviceID 设备标识信息,不需要注册,但是厂商需要指定
class 类型标识
subsystem vendorID 细分标识
subsystem deviceID 细分标识
阅读全文 »

类型

驱动移植的许多问题都是数据类型导致的,同样的数据类型在不同的平台上可能有不同的位宽。内核将内存看做一个巨型数组看看待,但是内核中表示地址往往用的unsigned long,这可以防止一些错误的解指针。同时在当前支持linux的各个平台上,long的位宽和指针位宽总是相同的。内核提供可移植类型定义,下面这个头文件包含u8u16u32u64Linux专用数据类型。

1
#include <linux/types.h>

内存

对内核的页面大小不要有任何假定,不要认为内核页面一定是4KB的。具体可以通过PAGE_SIZE判断。如果是用户态,可以用getpagesize库来获得页面大小。对于需要用页面幂次分配内存的参数,可以用get_order获得正确的幂次。

阅读全文 »

介绍

Kdump是一种内核崩溃转储机制,其依靠底层的kexec工作。kexec是一种内核套内核的系统启动机制,它会让系统启用两层内核。用户看到的内核在最里层,当用户内核崩溃的时候,处于外层的内核接管控制权限。此时,外层内核会执行一些数据保存工作。外层内核驻留在内存中,且这片内存对内层内核不可见。

安装

1
2
yum install kexec-tools crash -y
debuginfo-install kernel

配置

阅读全文 »

注意:该部分内容收录自互联网。非原创,但有自己的调整

  • .tar

    1
    2
    解包:tar xvf FileName.tar  
    打包:tar cvf FileName.tar DirName (注:tar是打包,不是压缩!)
  • .gz

    1
    2
    3
    解压1:gunzip FileName.gz  
    解压2:gzip -d FileName.gz
    压缩:gzip FileName
  • .tar.gz 和 .tgz

    1
    2
    3
    4
    5
    解压:tar zxvf FileName.tar.gz  
    压缩:tar zcvf FileName.tar.gz DirName

    多线程解压:tar --use-compress-program=pigz -xpf FileName.tar.gz
    多线程压缩:tar --use-compress-program=pigz -cpf FileName.tar.gz DirName
  • .bz2 和 .bz

    1
    2
    3
    解压1:bzip2 -d FileName.bz2  
    解压2:bunzip2 FileName.bz2
    压缩: bzip2 -z FileName
  • .tar.bz2 和 tar.bz

    1
    2
    解压:tar jxvf FileName.tar.bz2  
    压缩:tar jcvf FileName.tar.bz2 DirName
  • .xz

1
2
解压:xz -d FileName.xz
压缩:xz -z FileName
  • .tar.xz
1
2
解压:tar -xvJf FileName.tar.xz (注:网上说的分两次解压的说法是不必要的,当前tar可以一次性压缩和解压。)
压缩:tar -cvJf FileName.tar.xz
阅读全文 »

RedHat系列配置静态IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# cat  /etc/sysconfig/network-scripts/ifcfg-enp2s0f0 
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static # 重点
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=enp2s0f0
UUID=65d626bd-d33f-42aa-8180-c6259b552177
DEVICE=enp2s0f0
ONBOOT=yes # 重点
IPADDR=xx.xx.xx.xx # 重点
PREFIX=21 # 重点
GATEWAY=xx.xx.xx # 重点

PS:用nmtui工具也可以,好像还更方便

RedHat系列配置网桥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RedHat:~ # cat /etc/sysconfig/network-scripts/ifcfg-br0 
DEVICE=br0
TYPE=Bridge
BOOTPROTO=static
IPADDR=xx.xx.xx.xx
PREFIX=21
GATEWAY=xx.xx.xx

RedHat:~ # cat /etc/sysconfig/network-scripts/ifcfg-eth2
DEVICE=eth2
TYPE=Ethernet
BOOTPROTO=static
ONBOOT=yes
BRIDGE=br0
阅读全文 »

介绍

之前基于CentOS做了一些定制化项目,编译了多次内核,但是一直没有对RedHat给出的官方内核编译指导进行过仔细研究。之前编译内核往往是按照原版内核源码的经典编译方法来编译的,但事实上RedHat系列的发行版是有一套专门的内核编译工具的。rpmbuild,通过这个工具可以编译出debuginfo,debuginfo是crash调试用的。但是rpmbuild编译只针对RedHat系列官方发布的xxxx.src.rpm的编译,本文描述的就是这种场景。

首先要下载内核

源码包的下载地址可以从Sources.repo中找到

安装编译全家桶

阅读全文 »

介绍

本章描述巨细无比,但是忘了一个前提。那就是本文提到的那些外设,不管是通过端口访问,还是通过I/O内存访问。其在I/O端口或者I/O内存中的范围是既定的,这是理解本章的.

一个前提。正是如此,驱动使用诸如request_region或者request_mem_region这样的接口的入参才得以确定。事实上,外设的I/O地址空间或者I/O内存空间,既可以是硬件限定,也可是软件协商。

I/O端口

不同的CPU在和外设交互这一件事情上,有不同的设计。有的有独立的地址空间,有的有独立的总线,有的有独立的指令。但是由于I/O总线方式访问外设的思路太流行了,以至于linux内核以及各大硬件平台都有趋同的设计,使用类似I/O的外设访问方式。同时,即使外设总线为I/O保留了专门的地址空间,也不是所有的设备都把寄存器映射到I/O端口,这为编译器和CPU提供了更大的灵活性。

阅读全文 »

中断注册

中断,传统的中断信号线式的中断需要注册才可以用。信号线是各设备分时复用或者共享的,具体哪个设备使用哪个信号线是硬件连线决定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 注册中断信号线,返回非0值表示成功
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long flags, const char *dev_name, void *dev_id); // TODO 主动还是被动
// irq,中断号
// handler,中断处理函数
// flag,见下文
// flags,中断管理掩码
// dev_name,设备名称,中断的拥有者
// dev_id,自定义数据,驱动可以用dev_id区分设备,当flag含有SA_SHIRQ时该字段必须填充值

// 释放中断号
void free_irq(unsigned int irq, void *dev_id);

// x86架构还提供下面这接口,用于查询某个中断是否可以被注册。
// 但是注意,查询返回和真正注册不是原子的,换句话说,查询哪怕返回OK,但是到注册的时候可能情况已经变成不OK了
int can_request_irq(unsigned int irq, unsigned long flags);

推荐在打开设备的时候注册中断,而不是模块初始化的时候,这样可以提高中断信号线的复用效率。同时,还应在设备关闭时反注册中断。

flag可以有多个不同的取值,列表如下

阅读全文 »

扫描框架

提前扫描一下要看的内容包含哪些章节,哪些标题。先在脑子里建立一个基本脉络,然后去读内容的时候就不停的补全该脉络。

安静的环境

读书最好找一个安静的阅读环境,最好把手机、音乐等无关因素都丢得远远的以保持专心看书。如果注意力确实难以集中,可以用喝水、深呼吸等方式来集中注意力。

勤记笔记

阅读全文 »

说明

内存管理是操作系统的核心,在ldd3第八章仅介绍了kmalloc和vmalloc的用法,但是并没有过多的涉及其实现原理。后面会写一个较为细致的内存管理的笔记,作为本章笔记的补充。

per-cpu变量

per-cpu变量将会被内核放到一个特殊的内存段中。每个cpu都包含一份相同名称的变量,互不干扰,访问时几乎不用加锁。但在访问per-cpu变量是,要注意在关抢占的情况下完成所有的访问操作。注意,per-cpu不要求关中断,而要求关抢占。关抢占的意义是防止使用per-cpu的线程被调度到别的核心执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DEFINE_PER_CPU(type, name); // 定义一个常规的per-cpu变量
DEFINE_PER_CPU(int[3], my_percpu_array); // 可以是数组
void *alloc_percpu(type); // 动态的创建per-cpu变量
void *__alloc_percpu(size_t size, size_t align); // 动态创建per-cpu变量但是指定基地址对齐

/*
* Must be an lvalue. Since @var must be a simple identifier,
* we force a syntax error here if it isn't.
*/
#define get_cpu_var(var) \
(*({ \
preempt_disable(); \
this_cpu_ptr(&var); \
}))

/*
* The weird & is necessary because sparse considers (void)(var) to be
* a direct dereference of percpu variable (var).
*/
#define put_cpu_var(var) \
do { \
(void)&(var); \
preempt_enable(); \
} while (0)

// 还有指针访问形式,需要调用者手动禁止抢占,一般封装在get_put()和put_cpu()之间。
#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu)) // 访问指定CPU的per-cpu变量,需要用锁来保证安全
阅读全文 »