struct inode 结构体
在内核中,使用一个struct inode 结构体,来唯一的表示一个文件。
一个文件对应一个inode结构体。
struct inode 存储的是文件的属性相关的信息。
// 当执行一次 mknod 创建的是设备节点文件,此时生成一个 struct inode 结构体
struct inode {
umode_t i_mode; // 打开方式
unsigned short i_opflags;
kuid_t i_uid; // uid 用户id
kgid_t i_gid; // 组id
unsigned int i_flags; // 标志位
const struct inode_operations *i_op; // inode节点的操作方法集
struct super_block *i_sb;
struct address_space *i_mapping;
/* Stat data, not accessed from path walking */
unsigned long i_ino; // inode号码
union {
const unsigned int i_nlink; // 链接数
unsigned int __i_nlink;
};
dev_t i_rdev; // 设备号
loff_t i_size; // 大小
struct timespec i_atime; // 访问时间
struct timespec i_mtime; // 修改时间
struct timespec i_ctime; // 创建时间
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
const struct file_operations *i_fop; /* 文件操作方法集合指针*/
struct list_head i_devices; // 循环双链表
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev; // 字符设备地址指针
};
void *i_private; /*私有数据 */
};
struct file 结构体
struct file 也是一个文件属性信息结构体。应用程序每调用一次open函数,那么内核中生成一个struct file的结构体。
struct file {
struct path f_path; // 路径
struct inode *f_inode; /* cached value */
const struct file_operations *f_op; // 文件操作方法集合
unsigned int f_flags; // 文件操作标志位
fmode_t f_mode; // 方式
loff_t f_pos; // 偏移量
struct fown_struct f_owner; // 异步结构体
/* needed for tty driver, and maybe others */
void *private_data;
.....
};
open() ---> struct file |
open() ---> struct file | ---> struct inode --> 具体的文件
open() ---> struct file |
应用程序open 如何访问到驱动中 demo_open
struct inode 在内核中使用循环双链表进行管理的:
struct inode <---> struct inode <---> struct inode <---> struct inode ....
(设备号)
struct cdev <---> struct cdev <---> struct cdev <----> struct cdev 。。。。 // cdev 循环双链表
(设备号)
两条链表之间,相互探测,按设备号进行匹配,一旦匹配成功,将cdev的地址,fops地址,赋值给 struct inode 成员
循环双链表
// 循环双链表头结点定义
struct list_head {
struct list_head *next, *prev;
};
// 循环双链表结点初始化
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
// 插入节点,头插法
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
// 删除节点
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
// 遍历循环双链表
#define list_for_each(pos, head)
for (pos = (head)->next; pos != (head); pos = pos->next)
list_for_each(.. , ..)
{
。。。
}
// 尾插法
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
已知一个结构体成员,求出此结构体首地址
struct node {
int a;
char ch;
short b; // 绝对地址
int d;
};
int a;
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
功能:已知一个结构体的成员的地址,求出此结构体首地址
参数:ptr 已知的成员地址
type 结构体的类型
member 已知地址的对应的成员的名称
返回值:结构体首地址
/// 求成员的偏移量
offsetof(type,member) , // type 结构体类型 member 结构体成员名称
---->
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) // & 取出变量的地址, size_t 强制类型转换,将地址转换成一个整形数据
// typeof 已知成员的名称,求出它的类型
typeof( ((type *)0)->member ) *__mptr = (ptr); // short * __mptr = (ptr);
// (char *)__mptr 强制类型转换 ,字符指针地址
(type *)( (char *)__mptr - offsetof(type,member) );
并发的概念
并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源
(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions)
竞态的原因:
1,对称多处理器(SMP)的多个CPU
2,单CPU内进程与抢占它的进程
3,中断(硬中断、软中断、Tasklet、底半部)与进程之间
解决竞态的途径:
保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问。
访问共享资源的代码区域称为临界区(critical sections),临界区需要被以某种互斥机制加以保护。
互斥机制
1,中断屏蔽
2,原子操作
3,自旋锁
4,信号量
中断屏蔽
local_irq_disable() /* 屏蔽中断 /
. . .
critical section / 临界区*/
. . .
local_irq_enable() /* 开中断*/
// 注意点:local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断,因此,并不能解决SMP多CPU引发的竞态。
原子操作
对原子变量的操作是一气呵成。
int flag = 1;
demo_open()
{
if(flag == 1)
{
flag--; // flag = 0;
// 打开成功
}else
{
return -EBUSY;
}
}
整型原子操作:
设置原子变量的值
void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为i */
atomic_t v = ATOMIC_INIT(0); /* 定义原子变量v并初始化为0 */
获取原子变量的值
atomic_read(atomic_t *v); /* 返回原子变量的值*/
原子变量加/减
void atomic_add(int i, atomic_t *v); /* 原子变量增加i */
void atomic_sub(int i, atomic_t *v); /* 原子变量减少i */
原子变量自增/自减
void atomic_inc(atomic_t *v); /* 原子变量增加1 */
void atomic_dec(atomic_t *v); /* 原子变量减少1 */
操作并测试
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);
上述操作对原子变量执行自增、自减和减操作后(注意没有加)测试其是否为0,为0返回true,否则返回false。
操作并返回
/// #define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
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);
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值
自锁锁
自旋锁实际上是忙等锁
自旋锁可能导致系统死锁
自旋锁锁定期间不能调用可能引起进程调度的函数,不能睡眠,不能执行耗时操作。
定义自旋锁
spinlock_t lock;
初始化自旋锁
spin_lock_init(&lock)
获得自旋锁
spin_lock(&lock)
如果能够立即获得锁,它就马上返回,否则,它将自旋在那里
spin_trylock(&lock)
该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假
释放自旋锁
spin_unlock(&lock)
信号量
信号量是内核中用来保护临界资源的一种,信号量保护的资源中,可以进行睡眠。
互斥机制
相关头文件 <linux/semaphore.h>
定义信号量
struct semaphore sem;
初始化信号量
void sema_init(struct semaphore *sem, int val);
信号量的获取
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem); // 申请资源的过程中,可以被中断
int down_trylock(struct semaphore * sem);
信号量的释放
void up(struct semaphore * sem);
互斥体
使用方法及场合和信号量一致
初始化
struct mutex my_mutex;
mutex_init(&my_mutex);
获取互斥体
void inline _ _sched mutex_lock(struct mutex *lock);
释放互斥体
void __sched mutex_unlock(struct mutex *lock);
如何将字符设备驱动与硬件结合起来
1,查看原理图
LED3 GPX1_0
2,三星的芯片手册
GPX1CON 0x11000C20 // 控制寄存器,设置管脚模式 , 0x1 输出模式
GPX1DAT 0x11000C24 // 数据寄存器,
GPX1_0 [3:0] 低四位控制
3,led驱动编程
地址映射
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:offset 物理地址
size 大小
返回值:映射后的虚拟地址
static inline void iounmap(void __iomem *addr)
功能:解除映射
参数:addr 虚拟地址
static inline u32 readl(const volatile void __iomem *addr)
功能:读取寄存器的值
参数:虚拟地址
返回值:值
static inline void writel(unsigned int b, volatile void __iomem *addr)
功能:将数据写入寄存器
参数:b 数值
addr 寄存器的虚拟地址
返回值:无
流水灯
ioctl
while(1)
{
ioctl(fd, LED_ON , &num);
sleep(1);
ioctl(fd, LED_OFF, &num);
num++;
}
驱动:unlocked_ioctl