Linux中断分析2

PCI的I/O空间、PCI的存储空间和PCI的配置空间

I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。
内核在启动时负责对所有PCI设备进行初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所有找到的PCI设备,以及这些设备的参数和属性。

0x1000 — 0x1FFF

GICC_CTLR(0x0000):CPU Interface Control,控制中断是否传给CPU核,控制中断是IRQ还是FIQ

GICC_PMR(0x0004):Priority Mask,控制优先级门限,低于门限的中断不会转发到CPU

GICC_BPR(0x0008):Binary Point Register,是将优先级分成2部分:Group priority 、Subpriority,只使用优先级的Group priority部分,忽略Subpriority部分

详解:比如GIC-400的默认值是2,则优先级的[7:3]作为Group priority ,[2:0]作为Subpriority,则总共可生成32个优先级,与GICD_IPRIORITYRn保持一致

GICC_IAR(0x000C):Interrupt Acknowledge,只读,可以查看当前pending的最高优先级中断

GICC_EOIR(0x0010):End of Interrupt,只写,中断处理完成后需要写该寄存器

GICC_RPR(0x0014):Running Priority,只读,当前处理中断的优先级

GICC_HPPIR(0x0018):Highest Priority Pending ,只读,处于active状态的中断里优先级最高的

GICC_IIDR(0x00FC):ID寄存器,GIC-400的值是0x0202143B

GICC_DIR(0x1000):Deactivate Interrupt ,只写,移除中断的Active状态

GICC_IIDR(0x00FC):ID寄存器,GIC-400的值是0x0202143B

GICC_DIR(0x1000):Deactivate Interrupt ,只写,移除中断的Active状态

GICC_APRn 为在电源管理中保存和恢复状态提供支持 。

虚拟内存布局:

[ 0.000000] Memory: 1024MB = 1024MB total ---->内存的大小是1GB
[ 0.000000] Memory: 810820k/810820k available, 237756k reserved, 272384K highmem
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) ---->存放中断向量表
[ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) ---->固定映射区,留作特定用途
[ 0.000000] vmalloc : 0xef800000 - 0xfee00000 ( 246 MB) ---->vmalloc()分配内存的区
[ 0.000000] lowmem : 0xc0000000 - 0xef600000 ( 758 MB) ---->低端内存区,属于GFP_KERNEL标志
[ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) ---->永久映射,高端内存页框到内核地址空间的长期映射
[ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB) ---->#insmod *.ko module存放段
[ 0.000000] .text : 0xc0008000 - 0xc0a51018 (10533 kB) ---->代码段和只读的数据段
[ 0.000000] .init : 0xc0a52000 - 0xc0a8f100 ( 245 kB) ---->初始化段,使用__init修饰初始化函数
[ 0.000000] .data : 0xc0a90000 - 0xc0b297d8 ( 614 kB) ---->可读写的数据段
[ 0.000000] .bss : 0xc0b297fc - 0xc0d09488 (1920 kB) ---->未初始化的数据段
[ 0.000000] SLUB: Genslabs=11, HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1

cpu的模式:
模式切换:硬件自动完成
arm在irq/svc/abort几种模式下sp是不能共用的

user
用户模式
fiq

irq

svc

abt

und

sys

mon

31 30 29 28 27 26 25 24 23 20 19 16 15 10 9 8 7 6 5 4 0
N | Z| C |V |Q| IT J reser GE IT E A I F T M

M[4:0]
10000 user
10001 fiq
10010 irq
10011 svc
10111 abt
11011 und
11111 sys
10110 mon

此时lr中保存的实际上是异常的返回地址,异常发生,切换到svc模式后,会将lr保存到svc模式栈中

  • (pt_reg->pc),最后从异常返回时再将pt_reg->pc加载如arm寄存器pc中,实现异常返回。本函数只是
  • 其中一个步骤,即为将异常发生时刻lr保存到svc模式栈中(pt_reg->pc)做准备。
    *5. spsr是异常发生那一刻(即进入异常模式前是什么模式)的cpsr状态,如内核态下发生中断,则spsr是
  • svc模式下10011,如用户态下发生中断,则spsr是user模式10000。
    *6. 此时cpu正处于异常状态(如中断),此时cpsr为10010。
    *7. 要进行真正的异常处理,需要退出异常模式进入svc模式。

工作:
1)cpu状态的切换
cpu -> irq -> svc

2)在不同模式保存数据到栈中
sp — irq 报错lr 和 cpsr 信息
sp — svc

3)调用 handle_arch_irq 函数

    .macro vector_stub, name, mode, correction=0
    .align 5 //强制对齐32字节对齐

vector_\name:
.if \correction
/*
*需要调整返回值,对应irq异常将lr减去4,因为异常发生时,arm将pc地址+4赋值给了lr。
*/
sub lr, lr, #\correction
.endif

	@save r0,lr;将r0和lr保存到异常模式的栈上[sp] = r0;[sp+4] = lr_irq;
	
    stmia sp, {r0, lr} @ save r0, lr
    mrs lr, spsr //得到异常发生时所处模式得信息

	/*
	 *将spsr保存到异常模式的栈上[sp+8]=spsr_irq=lr
	*/
	str lr, [sp, #8] @ save spsr

	/*
	 *1. dabt处理时,r0=r0^(0x17^0x13)=r0^0x4,bit3取反之后10011变为svc模式;
	 *2. irq处理时:r0=10010=r0^(0x12^0x13)=r0^0x1=10011变为svc模式
	*/
	mrs r0, cpsr
	eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
	msr spsr_cxsf, r0


	/*
	*r0=sp;
	*注意:
	*1. 此时r0中保存了异常状态下sp栈地址,这个栈上保存了r0,lr(异常返回地址),spsr(异常发生时,cpu
	* 的状态,当然异常返回时需要恢复该状态)
	*2. 之后的函数会把r0中保存的异常模式的sp上信息,加载到svc模式下的sp栈上。异常处理返回时再将svc
	* mode 的栈加载到arm寄存器上。
	*/
	mov r0, sp
	//应该再切换到svc 之前把sp保存到r0中 ***************

	/*
	*lr中保存发生异常时arm的cpsr状态到spsr
	*1. usr模式发生异常则lr=10000&0x0f;lr=pc+lr<<2 pc+0时执行 __irq_usr;
	*2. svc模式发生异常则lr=10011&0x0f;lr=pc+lr<<2 pc+12时执行 __irq_svc
	*/
    ARM( ldr lr, [pc, lr, lsl #2] )

	/* movs中s表示把spsr恢复给cpsr,上面可知spsr保存的是svc模式,不过此时中断还是关闭的
	 * 异常处理一定要进入svc模式原因:
	 *(1)异常处理一定要PL1特权级。
	 *(2)使能嵌套中断。
	 * 如果一个中断模式(例如用户态发生中断,arm从usr进入irq模式)中重新允许中断,
	 * 且这个中断模式中使用了bl指令,bl会把pc放到lr_irq中,这个地址会被当前模式下产生的中断破坏
	 * 这种情况下中断无法返回。所以为了避免这种情况,中断处理过程应该切换到svc模式,bl指令可以把
	 * pc(即子程序返回地址)保存到lr_svc.
	 */
			movs pc, lr @ branch to handler in SVC mode
	ENDPROC(vector_\name)

    .long __irq_usr     @ 0 (USR_26 / USR_32) 从用户态下进入的irq,执行__irq_usr代码
    .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
    .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
    .long __irq_svc     @ 3 (SVC_26 / SVC_32) 从内核态下进入的irq,执行__irq_svc代码
    .long __irq_invalid @ 4 .long __irq_invalid @ 5
    .long __irq_invalid @ 6
    .long __irq_invalid @ 7
    .long __irq_invalid @ 8
    .long __irq_invalid @ 9
    .long __irq_invalid @ a
    .long __irq_invalid @ b
    .long __irq_invalid @ c
    .long __irq_invalid @ d
    .long __irq_invalid @ e
    .long __irq_invalid @ f

//此时arm处于svc模式执行下面代码
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler //irq处理函数
get_thread_info tsk
mov why, #0
b ret_to_user_from_irq //中断处理完成返回
UNWIND(.fnend )
ENDPROC(__irq_usr)/*

    .macro usr_entry, trace=1, uaccess=1

UNWIND(.fnstart )
UNWIND(.cantunwind ) @ don’t unwind the user space

	/*
	 *arch/arm/kernel/asm-offsets.c中定义DEFINE(S_FRAME_SIZE,sizeof(struct pt_regs)) 
	 *S_FRAME_SIZE=72
	*/
	sub sp, sp, #S_FRAME_SIZE

/*
*注释:
*宏CONFIG_MULTI_IRQ_HANDLER在.config中有定义,会将handle_arch_irq里的值赋值给pc去执行。
*给handle_arch_irq请看后面的C语言阶段分析
/
/

  • Interrupt handling.
    */
    .macro irq_handler
    #ifdef CONFIG_MULTI_IRQ_HANDLER

      //调用中断函数
      ldr r1, =handle_arch_irq
      mov r0, sp
      badr lr, 9997f
      ldr pc, [r1] //进入C语言阶段的中断处理
    

#else
arch_irq_handler_default
#endif
9997:
.endm

handle_arch_irq 赋值在/drivers/irqchip/irq-vic.c的vic_register函数中调用set_handle_irq进行赋值

static void __init vic_register(void __iomem *base, unsigned int parent_irq,
unsigned int irq,
u32 valid_sources, u32 resume_sources,
struct device_node *node)

	struct vic_device *v; 

	//The base address of the VIC
    v = &vic_devices[vic_id];
    v->base = base;
    v->valid_sources = valid_sources;
    v->resume_sources = resume_sources;
	
	//handle_arch_irq = vic_handle_irq 最终调用 vic_handle_irq 函数
    set_handle_irq(vic_handle_irq);  //此处被调用
    vic_id++;

//drivers/irqchip/irq-vic.c
/*

  • Keep iterating over all registered VIC’s until there are no pending

  • interrupts.
    */
    static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
    {
    int i, handled;

     do {
             for (i = 0, handled = 0; i < vic_id; ++i)
                     handled |= handle_one_vic(&vic_devices[i], regs);
     } while (handled);
    

}

static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
{
u32 stat, irq;
int handled = 0;

    while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
            irq = ffs(stat) - 1;
            handle_domain_irq(vic->domain, irq, regs);
            handled = 1;
    }

    return handled;

}

static inline int handle_domain_irq(struct irq_domain *domain,
unsigned int hwirq, struct pt_regs *regs)
{
return __handle_domain_irq(domain, hwirq, true, regs);
}

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
bool lookup, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
unsigned int irq = hwirq;

    if (unlikely(!irq || irq >= nr_irqs)) {
            ack_bad_irq(irq);
            ret = -EINVAL;
    } else {
            generic_handle_irq(irq);
    }

int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
            return -EINVAL;
    generic_handle_irq_desc(desc);

static inline void generic_handle_irq_desc(struct irq_desc desc)
{
/

*handle_irq在struct domain的struct domain_ops的map函数中调用
*irq_set_chip_and_handler 赋值为 handle_level_irq
*/
desc->handle_irq(desc);
}

void handle_level_irq(struct irq_desc *desc)
{
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);

    if (!irq_may_run(desc))
            goto out_unlock;

    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
    kstat_incr_irqs_this_cpu(desc);

    /*
     * If its disabled or no action available
     * keep it masked and get out of here
     */
    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
            desc->istate |= IRQS_PENDING;
            goto out_unlock;
    }
	
	//继续调
    handle_irq_event(desc);

//kernel/irq/chip.c
static inline void mask_ack_irq(struct irq_desc *desc)
{
/*desc->irq_data.chip在struct domain的struct domain_ops的map函数中调用
*irq_set_chip_and_handler赋值为vic_chip.
*/
if (desc->irq_data.chip->irq_mask_ack)
desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
else {
desc->irq_data.chip->irq_mask(&desc->irq_data);
if (desc->irq_data.chip->irq_ack)
desc->irq_data.chip->irq_ack(&desc->irq_data);
}
irq_state_set_masked(desc);
}

//drivers/irqchip/irq-vic.c 在
static struct irq_chip vic_chip = {
.name = “VIC”,
.irq_ack = vic_ack_irq,
.irq_mask = vic_mask_irq,
.irq_unmask = vic_unmask_irq,
.irq_set_wake = vic_set_wake,
};

//kernel/irq/handle.c
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
irqreturn_t ret;

    desc->istate &= ~IRQS_PENDING;
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    raw_spin_unlock(&desc->lock);

    ret = handle_irq_event_percpu(desc);

//kernel/irq/handle.c
irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
irqreturn_t retval = IRQ_NONE;
unsigned int flags = 0, irq = desc->irq_data.irq;
struct irqaction *action = desc->action;

    /* action might have become NULL since we dropped the lock */
    while (action) {
            irqreturn_t res;

            trace_irq_handler_entry(irq, action);
			
			
			
			
            /*
             *action->handler处理函数为驱动中注册的处理函数
             */
            res = action->handler(irq, action->dev_id);

interrupt-controler
interrupts = ;
#interrupt-cells = <3>
interrupt-controller;
interrupt-parent = <&gpc>;

interrupts = ,
,
,
;
interrupt-names = “gpmi0”, “gpmi1”, “gpmi2”, “gpmi3”;

interrupts = ;
interrupt-names = “bch”;

======================================中断向量表和中断处理部分代码的搬移

early_trap_init函数的调用流程为:
start_kernel(init/main.c)—>
setup_arch(arch/arm/kernel/setup.c)—>
paging_init(arch/arm/mm/mmu.c)—>
devicemaps_init(arch/arm/mm/mmu.c)—>
early_trap_init(arch/arm/kernel/traps.c)

static void __init devicemaps_init(const struct machine_desc *mdesc)
{
struct map_desc map;
unsigned long addr;
void vectors;
/

* Allocate the vector page early.
*分配两个页的内存空间,arm中每个页的大小为4K,这两个页的内存空间,一个是为保存中断向量
*表,一个是为了保存中断的处理部分代码,这两部分代码的排布可以在
*(arch/arm/kernel/vmlinux.lds和arch/arm/kernel/entry-armv.S)中可以具体分析出来
*/
vectors = early_alloc(PAGE_SIZE * 2);
early_trap_init(vectors);

    /*
     *创建一个页的内存地址映射,虚拟地址为0xffff0000,此地址为中断向量表的高端地址
     *设置中断向量表的高端地址在汇编的v7_setup中,使用的v7_crval设置了cp15的c1寄存器
     *v7_crval定义在arch/arm/mm/proc-v7-2level.S。
     */
    map.pfn = __phys_to_pfn(virt_to_phys(vectors));
    map.virtual = 0xffff0000;
    map.length = PAGE_SIZE;

#ifdef CONFIG_KUSER_HELPERS //此宏有定义
map.type = MT_HIGH_VECTORS;
#else
map.type = MT_LOW_VECTORS;
#endif
create_mapping(&map);
/*
*判断中断向量表的位置是否设置在高端地址,如果中断向量表没有设置在高端地址,
*在映射低端中断向量表地址。
*/
if (!vectors_high()) {
map.virtual = 0;
map.length = PAGE_SIZE * 2;
map.type = MT_LOW_VECTORS;
create_mapping(&map);
}

    /* Now create a kernel read-only mapping */
    map.pfn += 1;
    map.virtual = 0xffff0000 + PAGE_SIZE;
    map.length = PAGE_SIZE;
    map.type = MT_LOW_VECTORS;
    create_mapping(&map);








    .align  2
    .type   v7_crval, #object

v7_crval:
crval clear=0x2120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c

	/*
     * 将申请的4K先设置为未定义指令,防止在发生其他中断时,没有处理导致cpu错误
    */
    for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)
            ((u32 *)vectors_base)[i] = 0xe7fddef1;/*


    /*
     *将中断向量表和中断处理的代码搬移到申请的两页地址空间内
     */
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);

    kuser_init(vectors_base);

    flush_icache_range(vectors, vectors + PAGE_SIZE * 2);

void __init early_trap_init(void *vectors_base)
{

    unsigned long vectors = (unsigned long)vectors_base;
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    unsigned i;

	/*
     * 将申请的4K先设置为未定义指令,防止在发生其他中断时,没有处理导致cpu错误
    */
    for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)
            ((u32 *)vectors_base)[i] = 0xe7fddef1;


    /*
     *将中断向量表和中断处理的代码搬移到申请的两页地址空间内
    */
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);

    kuser_init(vectors_base);

    flush_icache_range(vectors, vectors + PAGE_SIZE * 2);

//init/main.c
asmlinkage void __init start_kernel(void)

{
……
trap_init(); //空函数
……

early_irq_init();

init_IRQ();

……

}

//arch/arm/kernel/irq/irqdesc.c/
int __init early_irq_init(void)
{
int i, initcnt, node = first_online_node;
struct irq_desc *desc;

    init_irq_default_affinity();  //由CONFIG_SMP宏决定是否为空函数,见下面分析一

    /* Let arch update nr_irqs and return the nr of preallocated irqs */
    initcnt = arch_probe_nr_irqs();//体系结构相关的代码来决定预先分配的中断描述符的个数见分析二
   
    if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))  //见下面分析三
            nr_irqs = IRQ_BITMAP_BITS;

    if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
            initcnt = IRQ_BITMAP_BITS;

    if (initcnt > nr_irqs)
            nr_irqs = initcnt;
			
	//事先构造好 irq_desc 结构体
    for (i = 0; i < initcnt; i++) {
            desc = alloc_desc(i, node, NULL);  //分配 struct irq_desc结构,分析四
            
			//用于标记那些irq 申请占用  allocated_irqs 关键变量
			set_bit(i, allocated_irqs);  	//设置相应的位数组,分析三
            irq_insert_desc(i, desc);  		//保存irq_desc指针,分析四
    }
    return arch_early_irq_init(); //kernel/softirq.c中定义,为空函数

}

//arch/arm/kernel/irq.c
#ifdef CONFIG_SPARSE_IRQ
int __init arch_probe_nr_irqs(void)
{
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;//NR_IRQS 16
return nr_irqs; //分析下面的分析,得知返回的是NR_IRQS
}
#endif

//kernel/irq/internals.h

//include/linux/types.h
#define DECLARE_BITMAP(name,bits)
unsigned long name[BITS_TO_LONGS(bits)]

//arch/arm/include/asm/bitops.h
#define set_bit(nr,p) ATOMIC_BITOP(set_bit,nr,p) //_set_bit
#define clear_bit(nr,p) ATOMIC_BITOP(clear_bit,nr,p)
#define change_bit(nr,p) ATOMIC_BITOP(change_bit,nr,p)
#define test_and_set_bit(nr,p) ATOMIC_BITOP(test_and_set_bit,nr,p)
#define test_and_clear_bit(nr,p) ATOMIC_BITOP(test_and_clear_bit,nr,p)
#define test_and_change_bit(nr,p) ATOMIC_BITOP(test_and_change_bit,nr,p)

/*

  • __builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量,
  • 如果是常数,函数返回1 ,否则返回0。此内置函数的典型用法是在宏中用于手动编译时优化
    /
    #ifndef CONFIG_SMP //此宏在.config中未定义
    /
  • The __* form of bitops are non-atomic and may be reordered.
    */
    #define ATOMIC_BITOP(name,nr,p)
    (__builtin_constant_p(nr) ? ___atomic##name(nr, p) : _##name(nr,p))
    #else
    #define ATOMIC_BITOP(name,nr,p) _##name(nr,p)
    #endif

//arch/arm/lib/setbit.S

#include
#include
#include “bitops.h”
.text
//bitop 为宏
bitop _set_bit, orr

//arch/arm/lib/bitops.h
.macro bitop, name, instr
ENTRY( \name )
UNWIND( .fnstart )
ands ip, r1, #3
strneb r1, [ip] @ assert word-aligned
mov r2, #1
and r3, r0, #31 @ Get bit offset
mov r0, r0, lsr #5
add r1, r1, r0, lsl #2 @ Get word offset
#if LINUX_ARM_ARCH >= 7 && defined(CONFIG_SMP)
.arch_extension mp
ALT_SMP(W(pldw) [r1])
ALT_UP(W(nop))
#endif
mov r3, r2, lsl r3
1: ldrex r2, [r1]
\instr r2, r2, r3
strex r0, r2, [r1]
cmp r0, #0
bne 1b
bx lr
UNWIND( .fnend )
ENDPROC(\name )
.endm

//driver/irqchip/irq-vic.c
/*
*通过IRQCHIP_DECLARE定义一个位于__irqchip_of_table段的全局量struct of_device_id
*__of_table_arm_pl192_vic,linux在中断初始化阶段在设备树中寻找是否有对应的节点,找到
*相对应的节点后,随即执行vic_of_init
*/
IRQCHIP_DECLARE(arm_pl192_vic, “arm,pl192-vic”, vic_of_init);

======================================内存分配

内核中内存申请的一些注意事项:
1)是否允许睡眠,在申请内存的时候
可以睡眠,指定 GFP_KERNEL。
不能睡眠,就指定 GFP_ATOMIC

2)是否允许DMA访问,
DMA 可以访问的内存,比如ISA或者有些PCI设备,就需要指定 GFP_DMA

3)检查返回地址
需要对返回值检查 NULL

4)内核申请的内存是否连续地址
内核通常需要 连续的物理地址

5)注意内存不能泄露
不能在死循环中申请内存,申请玩对应的内存,必须注意释放内存

1.kmalloc

kmalloc() 申请的内存位于物理内存映射区域(即虚拟内存地址)。但是在物理上它是
连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的
内存大小有限制,不能超过128KB。

都是以字节为单位进行分配。
所分配的内存,在虚拟地址上连续。

kmalloc 和 kzalloc 分配的内存大小有限制(128KB)

2.kzalloc
kzalloc() 函数与 kmalloc() 非常相似,参数及返回值是一样的,可以说是前者是后者的一个变

种,因为 kzalloc() 实际上只是额外附加了 __GFP_ZERO 标志。所以它除了申请内核内存外,还

会对申请到的内存内容清零。

kmalloc 和 kzalloc 分配的内存大小有限制(128KB)

kzalloc()函数一般用在Linux驱动代码中的 probe 函数中芯片驱动的内存开辟操作

都是以字节为单位进行分配。
所分配的内存,在虚拟地址上连续。

3.vmalloc()

vmalloc() 函数则会在 虚拟内存空间 给出一块连续的内存区,但这片连续的虚拟内存在

物理内存中并不一定连续。由于 vmalloc() 没有保证申请到的是连续的物理内存,因此对申请的内

存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。

vmalloc() 和 vfree() 可以睡眠,因此不能从中断上下文调用。

都是以字节为单位进行分配。
所分配的内存,在虚拟地址上连续。

vmalloc 分配内存时则可能产生阻塞

4、dma_alloc_wc

比如我们在学习Framebuffer驱动程序的时候,要分配显存(显存是物理地址连续的大内存,不能使用kmalloc)

====================== 内存读写函数

//具体代码参看arch/arm/include/asm/io.h
/*

  • Memory access primitives

  • These perform PCI memory accesses via an ioremap region. They don’t
  • take an address as such, but a cookie.
  • Again, this are defined to perform little endian accesses. See the
  • IO port primitives for more information.
    */
    #ifndef readl
    #define readb_relaxed© ({ u8 __r = __raw_readb©; __r; })
    #define readw_relaxed© ({ u16 __r = le16_to_cpu((__force __le16)
    __raw_readw©); __r; })
    #define readl_relaxed© ({ u32 __r = le32_to_cpu((__force __le32)
    __raw_readl©); __r; })

#define writeb_relaxed(v,c) __raw_writeb(v,c)
#define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c)
#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)

#define readb© ({ u8 __v = readb_relaxed©; __iormb(); __v; })
#define readw© ({ u16 __v = readw_relaxed©; __iormb(); __v; })
#define readl© ({ u32 __v = readl_relaxed©; __iormb(); __v; })

#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })

#define readsb(p,d,l) __raw_readsb(p,d,l)
#define readsw(p,d,l) __raw_readsw(p,d,l)
#define readsl(p,d,l) __raw_readsl(p,d,l)

#define writesb(p,d,l) __raw_writesb(p,d,l)
#define writesw(p,d,l) __raw_writesw(p,d,l)
#define writesl(p,d,l) __raw_writesl(p,d,l)

你可能感兴趣的:(linux,linux,运维,服务器)