嵌入式开发记录-补充01:linux内核同步
1、内核中的互斥与同步:
互斥:从排他性角度;同步:偏重顺序
2、内核中的同步互斥机制:
2.1、中断屏蔽:
local_irq_disable(); // 中断屏蔽临界区 local_irq_enable();
2.2、自旋锁:
临界区:不能有进程调度,执行时间短;
上锁失败:自旋(100%的CPU占有率)
// 初始化锁 spin_lock_init(_lock) spin_lock(& vcpu -> kvm -> mmu_lock) // 临界区 spin_unlock(spinlock_t * lock)
2.3、原子变量操作
atomic_t *v int atomic_read(const atomic_t *v) void atomic_set(atomic_t *v, int i) void atomic_add(int i, atomic_t *v) // 原子变量加i操作 void atomic_sub(int i, atomic_t *v) void atomic_inc(atomic_t *v) // 自增操作 void atomic_dec(atomic_t *v) int atomic_dec_and_test(atomic_t *v) int atomic_inc_and_test(atomic_t *v)
2.4、信号量
内核同步机制:解决多个任务同时访问一个共享资源引起的访问数据异步的问题;
信号量:信号量会引起进程睡眠,因此适应于锁长时间被持有的情况;因此不能在中断中使用;
1、信号量使用方式:
// 定义信号量 struct semaphore sem; // 初始化信号量 void sema_init(struct semaphore *sem,int val); // 初始化互斥信号量,只有一个资源的信号量,同时只有一个任务获得信号量 void init_MUTEX(struct semaphore *sem); // 等同于 sema_init(struct semaphore* sem, 1); /* 获取信号量 */ void down(struct semaphore* sem); // 进入睡眠的任务,不可被信号打断 void down_interruptible(struct semaphore *sem); // 进入睡眠的进程能够被信号打断,信号会导致该任务返回; int down_trylock(struct semaphore* sem); //如果丽可获取到锁,立刻放回;获取不到也不会导致该任务睡眠; /* 释放信号量 */ void up(struct semaphore* sem); // 释放信号量,唤醒等待者;
2.5、互斥锁:
struct mutex mutex_init(mutex) void mutex_destroy(struct mutex *lock) mutex_lock(struct mutex *lock); void mutex_unlock(struct mutex *lock); int __must_check mutex_lock_interruptible(struct mutex *lock);
3、 睡眠与唤醒
wait_event(queue,condition)
wait_event_interruptible(queue,condition)
wait_event_timeout(queue,condition,timeout)
wait_event_interruptible_timeout(queue,condition,timeout)
queue: wait_queue_head_t
condition:睡眠唤醒条件
timeout:超时限制
调佣以上函数,进程会把自己进入queue的等待队列,然后睡眠;
唤醒:condition为1,并且调用唤醒函数;
唤醒:
/* 唤醒所有给定队列上的进程 */ void wake_up(wait_queue_head_t* queue); /* 唤醒所有给定队列上可中断的进程 */ void wake_up_interruptible(wait_queue_head_t* queue);
4、将设备或者文件映射到用户空间
char* data; // 需要映射的内核内存 int myOpen(struct inode* p, struct file*p) { int i=0; struct page* mypage; data = kzalloc(8096,GFP_KERNEL); for(i=0;i<8092;i++) { data[i] = i%255; } for(i=0;i<2;i++) { mypage = virt_to_page((void*)(data+(i*PAGE_SIZE))); SetPageReserved(mypage); } return 0; }
static int myMmap(struct file* f,struct vm_area_struct* vma) { unsigned long phys, len; unsigned long pfn; phys = virt_to_phys((void*)data); // 将虚拟地址转换物理地址 len = vma->vma_end - vma_start; printk("vma end:%ld\n", vma->vma_end); printk("vma start:%ld\n", vma->vma_start); pfn = phys>> PAGE_SHIFT; // 得到物理块号; if(remap_pfn_range(vma,vma->vma_start,pfn,len,vma->vma_page_prot)) { printk("remap error"); } return 0; }
应用程序:
int fd = open("");
void* buf = mmap(...,"fd");
可直接修改读取buf中的数据; // 从设备或者文件映射过来的内存空间