唯客微博客

专注于计算机,嵌入式领域的技术

0%

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
28
29
30
31
32
33
34
35
36
37
/*
* 在内核<linux/circ_buf.h>中定义了关于环形缓冲区的相关变量。
* 其中缓冲区的大小size必须定义成2的n次方,这样可以将取余运算转换成位与运算,
* 提高计算机的处理速度。例如:x % size = x & (size - 1)。实际可用的空间为size-1,
* 这样可以避免缓冲区为满时和为空时都满足head = tail。
*/
struct circ_buf {
char *buf; // 指向分配缓冲区起始地址的指针
int head; // 指向生产者向缓冲区放入数据的位置
int tail; // 指向消费者从缓冲区取走数据的位置
};

/* Return count in buffer. */
/* 计算缓冲区中有效数据的大小; */
#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))

/* Return space available, 0..size-1. We always leave one free char
as a completely full buffer has head == tail, which is the same as
empty. */
/* 计算缓冲区剩余空闲空间的大小; */
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))

/* Return count up to the end of the buffer. Carefully avoid
accessing head and tail more than once, so they can change
underneath us without returning inconsistent results. */
/* 计算tail到缓冲区尾部的数据大小,即一次性可以读取的数据大小;*/
#define CIRC_CNT_TO_END(head,tail,size) \
({int end = (size) - (tail); \
int n = ((head) + end) & ((size)-1); \
n < end ? n : end;})

/* Return space available up to the end of the buffer. */
/* 计算head到缓冲区尾部的剩余空间,即一次性可以写入的数据大小; */
#define CIRC_SPACE_TO_END(head,tail,size) \
({int end = (size) - 1 - (head); \
int n = (end + (tail)) & ((size)-1); \
n <= end ? n : end+1;})

串口驱动API ## 1、uart_register_driver

1
2
3
4
5
6
/* 功能:    uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。
* 参数 drv:要注册的uart_driver

* 返回值: 成功,返回0;否则返回错误码
*/
int uart_register_driver(struct uart_driver *drv)
## 2、uart_unregister_driver
1
2
3
4
5
6
/* 功能:    uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数
* 参数 drv:要注销的uart_driver

* 返回值: 成功,返回0;否则返回错误码
*/
void uart_unregister_driver(struct uart_driver *drv)

3、uart_add_one_port

阅读全文 »



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
/*
* This structure describes all the operations that can be
* done on the physical hardware.
*/
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空。如果为空,函数应返回TIOCSER_TEMT,否则返回0。如果端口不支持此操作,返回TIOCSER_TEMT。*/
void (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口modem控制 */
unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口modem控制 */
void (*stop_tx)(struct uart_port *); /* 禁止串口发送数据 */
void (*start_tx)(struct uart_port *); /* 使能串口发送数据 */
void (*send_xchar)(struct uart_port *, char ch);/* 发送xChar */
void (*stop_rx)(struct uart_port *); /* 禁止串口接收数据 */
void (*enable_ms)(struct uart_port *); /* 使能modem的状态信号 */
void (*break_ctl)(struct uart_port *, int ctl); /* 设置break信号 */
int (*startup)(struct uart_port *); /* 启动串口,应用程序打开串口设备文件时,该函数会被调用 */
void (*shutdown)(struct uart_port *); /* 关闭串口,应用程序关闭串口设备文件时,该函数会被调用 */
void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); /* 设置串口参数 */
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate); /* 串口电源管理 */
int (*set_wake)(struct uart_port *, unsigned int state); /* */
const char *(*type)(struct uart_port *); /* 返回一描述串口类型的字符串 */
void (*release_port)(struct uart_port *); /* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */
int (*request_port)(struct uart_port *); /* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */
void (*config_port)(struct uart_port *, int); /* 执行串口所需的自动配置 */
int (*verify_port)(struct uart_port *, struct serial_struct *); /* 核实新串口的信息 */
int (*ioctl)(struct uart_port *, unsigned int, unsigned long); /* IO控制 */
};
## tx_empty(port) 此函数检查发送fifo和移位通过“端口”中描述的端口是否为空。如果为空,函数应返回TIOCSER_TEMT,否则返回0。如果端口不支持此操作,返回TIOCSER_TEMT。 + 锁:none + 中断:取决于调用者; + 此调用不休眠。

set_mctrl(port, mctrl)

此函数设置串口modem控制模式。mctrl相关的位是: - TIOCM_RTS RTS signal. - TIOCM_DTR DTR signal. - TIOCM_OUT1 OUT1 signal. - TIOCM_OUT2 OUT2 signal. - TIOCM_LOOP 设置端口为回环模式

阅读全文 »



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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
struct uart_port {
spinlock_t lock; /* 串口端口锁 */
unsigned int iobase; /* IO端口基地址 */
unsigned char __iomem *membase; /* IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */
unsigned int irq; /* 中断号 */
unsigned int uartclk; /* 串口时钟 */
unsigned int fifosize; /* 串口FIFO缓冲大小 */
unsigned char x_char; /* xon/xoff字符 */
unsigned char regshift; /* 寄存器位移 */
unsigned char iotype; /* IO访问方式 */
unsigned char unused1;

#define UPIO_PORT (0) /* IO端口 */
#define UPIO_HUB6 (1)
#define UPIO_MEM (2) /* IO内存 */
#define UPIO_MEM32 (3)
#define UPIO_AU (4) /* Au1x00 type IO */
#define UPIO_TSI (5) /* Tsi108/109 type IO */
#define UPIO_DWAPB (6) /* DesignWare APB UART */
#define UPIO_RM9000 (7) /* RM9000 type IO */

unsigned int read_status_mask; /* 关心的Rx error status */
unsigned int ignore_status_mask;/* 忽略的Rx error status */
struct uart_info *info; /* pointer to parent info */
struct uart_icount icount; /* 计数器 */

struct console *cons; /* console结构体 */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long sysrq; /* sysrq timeout */
#endif

upf_t flags;

#define UPF_FOURPORT ((__force upf_t) (1 << 1))
#define UPF_SAK ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK ((__force upf_t) (0x1030))
#define UPF_SPD_HI ((__force upf_t) (0x0010))
#define UPF_SPD_VHI ((__force upf_t) (0x0020))
#define UPF_SPD_CUST ((__force upf_t) (0x0030))
#define UPF_SPD_SHI ((__force upf_t) (0x1000))
#define UPF_SPD_WARP ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT ((__force upf_t) (1 << 29))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

unsigned int mctrl; /* 当前的moden设置 */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* 端口类型 */
const struct uart_ops *ops; /* 串口端口操作函数集 */
unsigned int custom_divisor;
unsigned int line; /* 端口索引 uart_driver.dev_name加上line组成串口的设备节点的名字 */
resource_size_t mapbase; /* IO内存物理基地址,可用于ioremap */
struct device *dev; /* 父设备 */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char unused[2];
void *private_data; /* 端口私有数据,一般为platform数据指针 */
};



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct uart_driver {
struct module *owner; /* 拥有该uart_driver的模块,一般为THIS_MODULE */
const char *driver_name; /* 串口驱动名,串口设备文件名以驱动名为基础 */
const char *dev_name; /* 串口设备名 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
int nr; /* 该uart_driver支持的串口个数(最大) */
struct console *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */

/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};

按照教程Linux内核调试环境搭建(基于ubuntu12.04) 配置kgdb双机调试时,出现一下错误:

1
2
3
4
5
6
7
8
9
10
11
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
0xffffffffb753b940 in ?? ()
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff82819ad0: file init/main.c, line 515.
(gdb) c
Continuing.
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0xffffffff82819ad0

解决方法是:在grub.cfg中添加rodata=off nokaslr

也就是在

阅读全文 »

 内存映射信息放在vma参数中,注意,这里的vma的数据类型是struct vm_area_struct,它表示的是一块连续的虚拟地址空间区域,在函数变量声明的地方,我们还看到有一个类似的结构体struct vm_struct,这个数据结构也是表示一块连续的虚拟地址空间区域。

那么,这两者的区别是什么呢?在Linux中,struct vm_area_struct表示的虚拟地址是给进程使用的,而struct vm_struct表示的虚拟地址是给内核使用的,它们对应的物理页面都可以是不连续的。struct vm_area_struct表示的地址空间范围是0~3G,而struct vm_struct表示的地址空间范围是(3G + 896M + 8M) ~ 4G。struct vm_struct表示的地址空间范围为什么不是3G~4G呢?原来,3G ~ (3G + 896M)范围的地址是用来映射连续的物理页面的,这个范围的虚拟地址和对应的实际物理地址有着简单的对应关系,即对应0~896M的物理地址空间,而(3G + 896M) ~ (3G + 896M + 8M)是安全保护区域(例如,所有指向这8M地址空间的指针都是非法的),因此struct vm_struct使用(3G + 896M + 8M) ~ 4G地址空间来映射非连续的物理页面。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
* This struct defines a memory VMM memory area. There is one of these
* per VM-area/task. A VM area is any part of the process virtual memory
* space that has a special rule for the page-fault handlers (ie a shared
* library, the executable area etc).
* 此结构定义了内存VMM内存区域。 每个VM区域/任务中有一个。 VM区域是进程虚拟内存空间的任何部分,
* 它具有页面错误处理程序的特殊规则(即共享库,可执行区域等)。
*/
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking.
第一个缓存行具有VMA树移动的信息 */

unsigned long vm_start; /* Our start address within vm_mm. 我们的起始地址在vm_mm内*/
unsigned long vm_end; /* The first byte after our end address within vm_mm. 我们的结束地址在vm_mm之后的第一个字节*/

/* linked list of VM areas per task, sorted by address
每个任务的VM区域的链接列表,按地址排序 */
struct vm_area_struct *vm_next, *vm_prev;

struct rb_node vm_rb;

/*
* Largest free memory gap in bytes to the left of this VMA.
* Either between this VMA and vma->vm_prev, or between one of the
* VMAs below us in the VMA rbtree and its ->vm_prev. This helps
* get_unmapped_area find a free area of the right size.
* 此VMA左侧最大的可用内存间隙(以字节为单位)。在此VMA和vma-> vm_prev之间,
* 或者在VMA rbtree中我们下面的一个VMA与其->vm_prev之间。
* 这有助于get_unmapped_area找到合适大小的空闲区域。
*/
unsigned long rb_subtree_gap;

/* Second cache line starts here.
第二个缓存行从这里开始 */

struct mm_struct *vm_mm; /* The address space we belong to. 我们所属的address space*/
pgprot_t vm_page_prot; /* Access permissions of this VMA. 此VMA的访问权限 */
unsigned long vm_flags; /* Flags, see mm.h. */

/*
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap interval tree.
* 对于具有地址空间(address apace)和后备存储(backing store)的区域,
* 链接到address_space->i_mmap间隔树,或者链接到address_space-> i_mmap_nonlinear列表中的vma。
*/
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;

/*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
* list, after a COW of one of the file pages. A MAP_SHARED vma
* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
* or brk vma (with NULL file) can only be in an anon_vma list.
* 在其中一个文件页面的COW之后,文件的MAP_PRIVATE vma可以在i_mmap树和anon_vma列表中。
* MAP_SHARED vma只能位于i_mmap树中。
* 匿名MAP_PRIVATE,堆栈或brk vma(带有NULL文件)只能位于anon_vma列表中。
*/
struct list_head anon_vma_chain; /* Serialized by mmap_sem &
* page_table_lock 由mmap_sem和* page_table_lock序列化*/
struct anon_vma *anon_vma; /* Serialized by page_table_lock 由page_table_lock序列化*/

/* Function pointers to deal with this struct.
用于处理此结构体的函数指针 */
const struct vm_operations_struct *vm_ops;

/* Information about our backing store:
后备存储(backing store)的信息:*/
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units 以PAGE_SIZE为单位的偏移量(在vm_file中)*/
struct file * vm_file; /* File we map to (can be NULL). 我们映射到文件(可以为NULL)*/
void * vm_private_data; /* was vm_pte (shared mem) 是vm_pte(共享内存) */

atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU mapping region NOMMU映射区域 */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA 针对VMA的NUMA政策 */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct结构使用链表或者树形结构链接,方便进程快速访问,如下图所示:

vm_area_struct结构中包含区域起始和终止地址以及其他相关信息,同时也包含一个vm_ops指针,其内部可引出所有针对这个区域可以使用的系统调用函数。这样,进程对某一虚拟内存区域的任何操作需要用要的信息,都可以从vm_area_struct中获得。mmap函数就是要创建一个新的vm_area_struct结构,并将其与文件的物理磁盘地址相连。

阅读全文 »

驱动实现mmap主要是调用:

1
2
int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t);

函数来映射,声明在include/linux/mm.h。 第一个参数:虚拟地址描述结构体(声明在include/linux/mm_types.h,起始mm.h中已经包含了它),一般是系统传递下来。 第二个参数:虚拟起始地址 第三个参数:物理地址 第四个参数:映射空间大小,单位字节 第五个参数:给新 VMA 要求的”protection”. 驱动直接使用 vma->vm_page_prot

返回值,成功返回0,否则返回-1;

阅读全文 »

SetPageReserved()   随着linux的长时间运行,空闲页面会越来越少,为了防止linux内核进入请求页面的僵局中,Linux内核采用页面回收算法(PFRA)从用户进程和内核高速缓存中回收内存页框,并根据需要把要回收页框的内容交换到磁盘上的交换区。调用该函数可以使页面不被交换。

1
#define SetPageReserved(page) set_bit(PG_reserved,&(page)->flags)
PG_reserved 的标志说明如下。 + PG_reserved is set for special pages, which can nEVEr beswapped out. Some of them might not EVEn exist (eg empty_bad_page)…

Linux内核和驱动开发时,由于各种结构体变量见相互引用,因此,当回收内存时,由于引用没有清理,很容易发生内存指向错误。因此,Linux使用引用计数方式来代替简单的malloc,free。不过由于Linux的内核是以C为开发语言,不是C++,无法使用继承和派生,因此只能使用结构体互相包含和containerof方式来使用引用计数框架,读起来还是比较晦涩的。 **.11

.h**

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
/*
引用计数的实现结构体
*/


#ifndef _KREF_H_
#define _KREF_H_

#ifdef __KERNEL__

#include <linux/types.h>
#include <asm/atomic.h>

/* 引用计数的原理很简单,使用时加1,不用时减1,如果减为0
则调用释放函数。
*/
struct kref {
/* Linux 使用原子变量来计数,这样可以防止多CPU,多进程时的竞态现象发生 */
atomic_t refcount;
};

void kref_init(struct kref *kref);/* 初始化一个引用计数结构体 */
void kref_get(struct kref *kref);/* 增加一个引用计数 */
void kref_put(struct kref *kref, void (*release) (struct kref *kref));/* 减少一个引用计数 */

#endif /* __KERNEL__ */
#endif /* _KREF_H_ */

.11.c

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <linux/kref.h>
#include <linux/module.h>

/**
* kref_init - initialize object.
* @kref: object in question.
*/
/* 初始化一个引用计数,这里可以看到引用计数初始化为1 */
void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount,1);
}

/**
* kref_get - increment refcount for object.
* @kref: object.
*/
/* 增加一个引用计数,可以发现这里是原子操作 */
void kref_get(struct kref *kref)
{
WARN_ON(!atomic_read(&kref->refcount));
atomic_inc(&kref->refcount);
}

/**
* kref_put - decrement refcount for object.
* @kref: object.
* @release: pointer to the function that will clean up the object when the
* last reference to the object is released.
* This pointer is required, and it is not acceptable to pass kfree
* in as this function.
*
* Decrement the refcount, and if 0, call release().
*/
;/* 减少一个引用计数 */
void kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
WARN_ON(release == NULL);
WARN_ON(release == (void (*)(struct kref *))kfree);

if (atomic_dec_and_test(&kref->refcount))
release(kref);/* 如果引用计数为0,就调用释放函数 */
}
阅读全文 »