1. 阻塞
阻塞是一种同步机制,当进程需要等待某个条件满足时,会进入休眠状态,直到条件满足时被唤醒。在Linux内核中,阻塞机制主要通过等待队列(wait_queue)实现。
初始化等待队列:
数据类型:wait_queue_head_t
初始化:#define init_waitqueue_head(q)
内部调用__init_waitqueue_head,需要传递一个等待队列头结构体指针。
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;
extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);
#define init_waitqueue_head(q) \do { \
static struct lock_class_key __key; \
__init_waitqueue_head((q), #q, &__key); \} while (0)
休眠等待:
wait_event:wait_event(wq, condition)
wq:等待队列
condition:条件唤醒,1时结束等待,0时继续等待
wait_event_interruptible:类似wait_event,但可以被信号中断
#define wait_event(wq, condition) \do { \
if (condition) \
break; \
wait_event_begin(wq, condition, TASK_UNINTERRUPTIBLE, 0); \} while (0)
#define wait_event_interruptible(wq, condition) \({ \
int __ret = 0; \
if (!(condition)) \
__ret = wait_event_interruptible_begin(wq, condition, TASK_INTERRUPTIBLE, 0); \
unlikely(__ret); \})
唤醒进程:
wake_up_interruptible:#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
wake_up:#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
x:等待队列,需要传递一个等待队列指针
#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
注意事项:
1.sleep_on方式需要wake_up唤醒,因为sleep_on设置进程状态为TASK_UNINTERRUPTIBLE。
2.wait_event与wait_event_interruptible在唤醒时需要设置condition为1(非0),否则不能唤醒进程。
3.wake_up_interruptible不能唤醒wait_event休眠的进程,wait_event设置状态为TASK_UNINTERRUPTIBLE,而wait_event_interruptible设置状态为TASK_INTERRUPTIBLE。
4.wake_up可以唤醒TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE,因为TASK_NORMAL == TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE。
5.建议使用wait_event_interruptible休眠方式。
实例:
hello.c
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include "common.h"
#define SIZE 100
int g_major = 0;
int g_min = 0;
int g_iDevNo = 0;
int g_iCount = 2;
struct cdev *g_pDev = NULL;
char data[SIZE] = "cc pp hh xx cq cf jh zy";
int g_condition = 0; //g_condition:条件唤醒,1时结束等待 ,0 :继续等待
struct class *g_pClass = NULL;
wait_queue_head_t g_queue; //初始化等待队列
int HelloOpen (struct inode *pNode, struct file *pFile)
{
printk("hello open enter\r\n");
printk("major %d, minor %d\r\n", MAJOR(pNode->i_rdev), MINOR(pNode->i_rdev));
printk("hello open exit\r\n");
g_condition = 0; //0 :继续等待
return 0;
}
int HelloClose(struct inode *pNode, struct file *pFile)
{
printk("hello close enter\r\n");
printk("major %d, minor %d\r\n", MAJOR(pNode->i_rdev), MINOR(pNode->i_rdev));
printk("hello close exit\r\n");
g_condition = 0; //0 :继续等待
return 0;
}
ssize_t HelloRead(struct file *pFile, char __user *buf, size_t count, loff_t *pos)
{
printk("hello read enter\r\n");
wait_event_interruptible(g_queue, g_condition); //休眠等待
if (count > SIZE - 1)
{
count = SIZE - 1;
}
if (copy_to_user(buf, data, count))
{
printk("copy to user error\r\n");
return -EINVAL;
}
printk("hello read exit\r\n");
return count;
}
ssize_t HelloWrite(struct file *pFile, const char __user *buf, size_t count, loff_t *pos)
{
printk("hello write enter\r\n");
if (count > SIZE -1)
{
count = SIZE -1;
}
if (copy_from_user(data, buf, count))
{
return -EINVAL;
}
printk("data is %s\r\n", data);
g_condition = 1; //1时结束等待
wake_up(&g_queue); //唤醒进程
printk("hello write exit\r\n");
return count;
}
long HelloIOctl(struct file *pFile, unsigned int cmd, unsigned long arg)
{
printk("hello io ctl enter\r\n");
switch (cmd)
{
case TEST_CMD:
{
printk("test cmd\r\n");
}
break;
case TEST_CMD1:
{
printk("test cmd1\r\n");
}
break;
case TEST_CMD2:
{
printk("test cmd2 %ld\r\n", arg);
}
break;
default:
{
printk("command error\r\n");
}
}
printk("hello io ctl exit\r\n");
return 0;
}
struct file_operations g_stFileOp =
{
.owner = THIS_MODULE,
.open = HelloOpen,
.release = HelloClose,
.read = HelloRead,
.write = HelloWrite,
.unlocked_ioctl = HelloIOctl,
};
int HelloModuleInit(void)
{
int iRet = -1;
int i = 0;
struct device *pDevTmp = NULL;
printk("hello module enter\r\n");
//register dev no
iRet = alloc_chrdev_region(&g_iDevNo,0, g_iCount, "hehe");
if (iRet != 0)
{
printk("register dev error\r\n");
return -1;
}
g_major = MAJOR(g_iDevNo);
printk("major %d, devno %d\r\n", MAJOR(g_iDevNo), g_iDevNo);
//alloc char dev
g_pDev = cdev_alloc();
if (!g_pDev)
{
printk("cdev alloc error\r\n");
goto CDEV_ALLOC_ERROR;
}
//init char dev
cdev_init(g_pDev, &g_stFileOp);
//add char dev to kernel
iRet = cdev_add(g_pDev, g_iDevNo, g_iCount);
if (iRet != 0)
{
printk("cdev add error\r\n");
goto CDEV_ADD_ERROR;
}
//create class
g_pClass = class_create(THIS_MODULE, "haha");
if (IS_ERR(g_pClass))
{
printk("create class error\r\n");
goto CDEV_ADD_ERROR;
}
i = 0;
for (i = 0; i < g_iCount; i++)
{
pDevTmp = device_create(g_pClass, NULL, MKDEV(g_major, i), NULL, "haha%d", i);
if (IS_ERR(pDevTmp))
{
goto DEVICE_CREATE_ERROR;
}
}
//init queue
init_waitqueue_head(&g_queue); //初始化等待队列头
printk("hello module exit\r\n");
return 0;
DEVICE_CREATE_ERROR:
i = 0;
for (i = 0; i < g_iCount; i++)
{
device_destroy(g_pClass, MKDEV(g_major, i));
}
class_destroy(g_pClass);
g_pClass = NULL;
CDEV_ADD_ERROR:
cdev_del(g_pDev);
g_pDev = NULL;
CDEV_ALLOC_ERROR:
unregister_chrdev_region(g_iDevNo, g_iCount);
return -1;
}
void HelloModuleExit(void)
{
int i = 0;
printk("exit module enter\r\n");
i = 0;
for (i = 0; i < g_iCount; i++)
{
device_destroy(g_pClass, MKDEV(g_major, i));
}
class_destroy(g_pClass);
g_pClass = NULL;
cdev_del(g_pDev);
g_pDev = NULL;
unregister_chrdev_region(g_iDevNo, g_iCount);
printk("exit module exit\r\n");
}
MODULE_LICENSE("GPL");
module_init(HelloModuleInit);
module_exit(HelloModuleExit);

make->sudo insmod hello.ko->lsmod->dmesg | tail

test.c->test_r

gcc -o test_r test.c ->sudo ./test_r dev/haha0
此时阻塞,dmesg | tail:

test1.c->test_w

gcc -o test_w test1.c ->sudo ./test_r dev/haha1
这时haha0阻塞解除
dmesg | tail:

2. 非阻塞
非阻塞是一种异步机制,进程不会因等待某个条件而进入休眠状态,而是立即返回。在应用层需要设置O_NONBLOCK标志,在驱动中可以通过struct file的f_flags成员获取此标志。
int flags = fcntl(fd, F_GETFL, 0);
if (flags & O_NONBLOCK) {// 非阻塞模式}
实例:
hello.c








3. poll
poll机制允许进程在同一时间等待多个文件描述符的事件。在驱动中实现file_operations结构体的poll函数指针,通过poll_wait可以将打开的file结构体和等待队列与传入的poll_table添加到内核中。
poll_wait函数:
static inline void poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) {
if (p && p->qproc) {
spin_lock_irq(&p->qproc->wait_lock);
p->qproc->nwait++;
spin_unlock_irq(&p->qproc->wait_lock);
}
add_wait_queue(wait_address, &p->wait);}
返回状态:
驱动的poll函数应该返回设备的当前状态,常用的状态值有:
POLLIN:可读
POLLOUT:可写
POLLRDNORM:普通数据可读
POLLERR:错误
static unsigned int device_poll(struct file *filp, poll_table *wait) {
poll_wait(filp, &device_wait_queue, wait);
if (device_data_available()) {
return POLLIN | POLLRDNORM;
}
return 0;}
4. 异步
异步机制允许设备驱动在事件发生时发送信号通知进程。
在驱动中实现file_operations结构体的fasync函数指针,通过fasync_helper将文件描述符加入到异步队列中。
fasync函数
static int device_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &device_fasync_queue);
}
发送信号
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
if (*fp)
{
struct fasync_struct *fa = *fp;
while (fa)
{
kill_pid_info(sig, 0, fa->pid);
fa = fa->fa_next;
}
}
}
通过这些机制,可以在驱动程序中实现高效的同步和异步操作。
fasync_helper() 是 Linux 内核中的一个辅助函数,用于实现文件描述符的异步通知(asynchronous I/O,AIO)。这个函数在处理文件描述符的异步 I/O 时非常有用,它可以将文件描述符加入或移出异步通知队列。当文件描述符的状态发生变化时(例如文件可读或可写),内核会发送信号给相应的进程。
函数原型:
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);
参数说明:
fd:文件描述符。这个参数通常是从用户空间传入的。
filp:指向 struct file 的指针。这个结构体表示一个打开的文件,包含了文件的各种属性和操作方法。
on:一个整数,用于控制是否开启或关闭异步通知。如果 on 为 1,则开启异步通知;如果为 0,则关闭异步通知。
fapp:一个指向 struct fasync_struct 指针的指针。这个结构体用于管理异步通知队列。
返回值:
0:操作成功。
-EINVAL:参数无效。
-EBADF:文件描述符无效。
-ENOMEM:内存不足。
-ENOLINK:无异步通知支持。
547

被折叠的 条评论
为什么被折叠?



