(1)非阻塞IO模型
(2)阻塞IO模型
(3)IO多路复用
(4)异步通知
非阻塞IO模型:当应用程序通过非阻塞的方式打开文件的时候,通过read读取
数据的时候,不管数据是否准备好,都要立即返回。
fd = open("/dev/mycdev0",O_RDWR|O_NONBLOCK); //使用非阻塞的方式打开文件
read(fd,buf,sizeof(buf));
--------------------------------------------------------
driver_read(struct file *file,,,)
{if(file->f_flags &O_NONBLOCK){//非阻塞,不管数据是否准备好都立即将数据拷贝到用户空间}
}
阻塞IO模型:应用层以阻塞的方式打开设备文件,然后调用read函数读取硬件的数据。
如果硬件的数据没有准备好,需要让进程进入到休眠的状态。当硬件的数据准备好的
时候产生中断,在中断的处理函数中唤醒休眠的进程即可。
fd = open("/dev/mycdev0",O_RDWR); //使用阻塞的方式打开文件
read(fd,buf,sizeof(buf));
--------------------------------------------------------
driver_read(struct file *file,,,)
{if(file->f_flags &O_NONBLOCK){//非阻塞}else{//阻塞,让进程进入休眠状态}
}中断的处理函数:唤醒休眠的进程
1.定义等待队列头wait_queue_head_t wq;
2.初始化等待队列头init_waitqueue_head(&wq);
3.调用对应的函数完成阻塞(定义等待队列项,将等待队列项放到队列的队尾,进程休眠)wait_event(wq, condition) //让进程进入到不可中断的等待态wait_event_interruptible(wq, condition) //让进程进入到可中断的等待态//返回值:如果是数据准备好了它返回0,如果是信号唤醒的进程的休眠返回-ERESTARTSYS注:condition代表的是数据是否准备好的标志,如果condition为真代表数据准备好了进程不需要休眠,如果condition为假代表数据没有准备好进程需要进入休眠的状态。
4.唤醒休眠的进程condition=1;wake_up(&wq);wake_up_interruptible(&wq);注:上述的两个函数唤醒的是等待队列,队列中的进程是否真的被唤醒取决于condition的真假,如果condition为真,进程就准备被唤醒了,如果condition为假进程没有被唤醒,进程继续休眠。
mycdev.c
#include
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
struct cdev* cdev;
unsigned int major = 511;
unsigned int minor = 0;
const int count = 3;
struct class* cls;
struct device* dev;
char kbuf[128] = { 0 };wait_queue_head_t wq; //定义等待队列头
int condition = 0; //数据是否准备好的状态,默认没有准备好
int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
ssize_t mycdev_read(struct file* file,char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);//判断用户是否阻塞的方式打开的设备文件if (file->f_flags & O_NONBLOCK) {//非阻塞return -EINVAL;} else {//阻塞//如果是数据准备好了它返回0,如果是信号唤醒的进程的休眠返回-ERESTARTSYSret = wait_event_interruptible(wq, condition);if (ret) {//使用Ctrl+c结束进程时 会打印下面这句话printk("receive signal.....\n");return ret;}}//将数据拷贝到用户空间if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error\n");return -EIO;}// 传递完数据 将condition清零 等待数据准备condition=0;return size;
}
ssize_t mycdev_write(struct file* file,const char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data form user error\n");return -EIO;}//当得到用户空间的数据 将condition置为1 代表数据准备好了 同时唤醒正在休眠的进程//驱动的write可以唤醒驱动的readcondition = 1; wake_up_interruptible(&wq);//唤醒的工作return size;
}
int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
const struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,
};
static int __init mycdev_init(void)
{int ret, i;dev_t devno;// 1.分配对象cdev = cdev_alloc();if (cdev == NULL) {printk("cdev alloc memory error\n");ret = -ENOMEM;goto ERR1;}// 2.初始化对象cdev_init(cdev, &fops);// 3.申请设备号if (major > 0) {//静态指定ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);if (ret) {printk("static:request device number error\n");goto ERR2;}} else {//动态申请ret = alloc_chrdev_region(&devno, 0, count, CNAME);if (ret) {printk("dynamic:request device number error\n");goto ERR2;}major = MAJOR(devno);minor = MINOR(devno);}// 4.注册ret = cdev_add(cdev, MKDEV(major, minor), count);if (ret) {printk("register char device driver error\n");goto ERR3;}// 5.自动创建设备节点cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("class create error\n");ret = PTR_ERR(cls);goto ERR4;}for (i = 0; i < count; i++) {dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);if (IS_ERR(dev)) {printk("device create error\n");ret = PTR_ERR(dev);goto ERR5;}}init_waitqueue_head(&wq); //初始化等待队列头return 0; /*!!!!!!!!!!!!!!!不要忘记写!!!!!!!!!!!!!!!*/
ERR5:for (--i; i >= 0; i--) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);
ERR4:cdev_del(cdev);
ERR3:unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:kfree(cdev);
ERR1:return ret;
}
static void __exit mycdev_exit(void)
{int i;for (i = 0; i < count; i++) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);cdev_del(cdev);unregister_chrdev_region(MKDEV(major, minor), count);kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include char buf[128] = "i am test block IO mode....";
int main(int argc, const char* argv[])
{int fd;pid_t pid;if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)PRINT_ERR("open error");//使用父子进程验证 write唤醒readpid = fork();if (pid == -1) {PRINT_ERR("fork error");} else if (pid == 0) {while (1) {memset(buf, 0, sizeof(buf));read(fd, buf, sizeof(buf));printf("buf = %s\n", buf);}} else {while(1){//保证子进程中的read先执行sleep(3);write(fd,buf,sizeof(buf));}wait(NULL);}close(fd);return 0;
}
//让进程进入到可中断的等待态 (信号)
//wq_head:等待队列头
//condition:如果condition为真代表数据准备好,如果condition为假代表数据没有准备好
//返回0代表数据准备好,如果返回-ERESTARTSYS代表信号唤醒的休眠
#define wait_event_interruptible(wq_head, condition)
({ int __ret = 0; //如果condition为假if语句成立,如果condition为真if不成立if (!(condition)) __ret = __wait_event_interruptible(wq_head, condition); __ret; //这个就是宏的返回值
})#define __wait_event_interruptible(wq_head, condition) ___wait_event(wq_head, condition, TASK_INTERRUPTIBLE, 0, 0, schedule())#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)
({ //1.定义等待队列项的结构体struct wait_queue_entry __wq_entry; long __ret = ret;//返回值的变量 //初始化等待队列项/*void init_wait_entry(struct wait_queue_entry *wq_entry, int flags){wq_entry->flags = flags; //0wq_entry->private = current; //进程的结构体 task_structwq_entry->func = autoremove_wake_function; //唤醒的函数INIT_LIST_HEAD(&wq_entry->entry); //构成队列的链表}*/init_wait_entry(&__wq_entry,0); for (;;) { //1.将等待队列项放到等待队列头之后//2.将进程的状态标记为可中断的等待态(此时进程没有真正的休眠)long __int = prepare_to_wait_event(&wq_head, &__wq_entry, TASK_INTERRUPTIBLE);if (condition) //数据是否准备好,如果准备好就退出循环 break; if (signal_pending_state(state, current)) { //判断是否收到了信号,如果收到信号退出循环 list_del_init(&wq_entry->entry);__ret = -ERESTARTSYS;; goto __out; } schedule();//让进程进入休眠状态的函数 //主动放弃cpu,进程真正进入到休眠状态} finish_wait(&wq_head, &__wq_entry); //将等待队列项从等待队列上删除//将进程的状态标记为运行态__out: __ret;
})
IO多路复用:在同一个进程中想同时监听多个硬件的数据,此时就需要使用IO多路复用
的机制。将需要监听的文件描述符放在需要监听文件描述符的表中,通过select/poll/epoll
完成监听,如果所有的硬件的数据都没有准备好此时进程休眠。当有硬件的数据准备好的时候
会产生硬件的中断,在中断的处理函数中唤醒休眠的进程,当进程被唤醒之后从准备好的文件
描述符表中找出准备好的文件描述符,从里面读取数据即可。
user:fd1 = open("/dev/mycdev0",O_RDWR);fd2 = open("/dev/input/mouse0",O_RDWR);fd_set rfds; //定义读表FD_ZERO(&rfds); //清空表FD_SET(fd1,&rfds);//将fd1放入表中FD_SET(fd2,&rfds)//将fd2放入表中select(maxfd+1,&rfds,NULL,NULL,NULL)if(FD_ISSET(fd1,&rfds)){//fd1数据准备好了read(fd1,buf1,sizeof(buf1));}if(FD_ISSET(fd2,&rfds)){//fd2数据准备好了read(fd2,buf2,sizeof(buf2));}
----------------------------------------------
kernel:fops: //应用层的select/poll/epoll对应的都是驱动中的poll函数grep ".poll =" * -nR__poll_t (*poll) (struct file *file, struct poll_table_struct *wait);{1.定义返回值的变量__poll_t mask = 0;2.调用poll_wait(是和阻塞相关的函数)poll_wait(file, 等待队列头, wait);3.判断数据是否准备好了if(condition){mask |= EPOLLIN; //EPOLLIN 数据可读 EPOLLOUT数据可写}4.返回maskreturn mask;}
test.c
#include
#include
char buf[128] = "i am test block IO mode....";
int main(int argc, const char* argv[])
{int fd1, fd2, ret;fd_set rfds; //定义读表if ((fd1 = open("/dev/mycdev0", O_RDWR)) == -1)PRINT_ERR("open error");if ((fd2 = open("/dev/input/mouse0", O_RDWR)) == -1)PRINT_ERR("open error");while (1) {FD_ZERO(&rfds); //清空表FD_SET(fd1, &rfds); //将fd1放入表中FD_SET(fd2, &rfds); //将fd2放入表中ret = select(fd2 + 1, &rfds, NULL, NULL, NULL);if (ret == -1)PRINT_ERR("select error");if (FD_ISSET(fd1, &rfds)) {// fd1数据准备好了memset(buf, 0, sizeof(buf));read(fd1, buf, sizeof(buf));printf("mycdev0:%s\n", buf);}if (FD_ISSET(fd2, &rfds)) {// fd2数据准备好了memset(buf, 0, sizeof(buf));read(fd2, buf, sizeof(buf));printf("mouse0:%s\n", buf);}}close(fd1);return 0;
}
mycdev.c
#include
#include
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
struct cdev* cdev;
unsigned int major = 511;
unsigned int minor = 0;
const int count = 3;
struct class* cls;
struct device* dev;
char kbuf[128] = { 0 };wait_queue_head_t wq; //定义等待队列头
int condition = 0; //数据是否准备好的状态,默认没有准备好
int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
ssize_t mycdev_read(struct file* file,char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);//将数据拷贝到用户空间if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error\n");return -EIO;}//将condition清零condition=0;return size;
}
ssize_t mycdev_write(struct file* file,const char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data form user error\n");return -EIO;}condition = 1; wake_up_interruptible(&wq);//唤醒的工作return size;
}
__poll_t mycdev_poll(struct file *file, struct poll_table_struct *wait)
{//定义返回值的变量__poll_t mask = 0;// 调用poll_waitpoll_wait(file, &wq_head, wait);// 判断数据是否准备好了if (condition) {mask |= EPOLLIN; // EPOLLIN 表示数据可读 EPOLLOUT代表数据可写}//返回maskreturn mask;
}
int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
const struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.poll = mycdev_poll,.release = mycdev_close,
};
static int __init mycdev_init(void)
{int ret, i;dev_t devno;// 1.分配对象cdev = cdev_alloc();if (cdev == NULL) {printk("cdev alloc memory error\n");ret = -ENOMEM;goto ERR1;}// 2.初始化对象cdev_init(cdev, &fops);// 3.申请设备号if (major > 0) {//静态指定ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);if (ret) {printk("static:request device number error\n");goto ERR2;}} else {//动态申请ret = alloc_chrdev_region(&devno, 0, count, CNAME);if (ret) {printk("dynamic:request device number error\n");goto ERR2;}major = MAJOR(devno);minor = MINOR(devno);}// 4.注册ret = cdev_add(cdev, MKDEV(major, minor), count);if (ret) {printk("register char device driver error\n");goto ERR3;}// 5.自动创建设备节点cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("class create error\n");ret = PTR_ERR(cls);goto ERR4;}for (i = 0; i < count; i++) {dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);if (IS_ERR(dev)) {printk("device create error\n");ret = PTR_ERR(dev);goto ERR5;}}init_waitqueue_head(&wq); //初始化等待队列头return 0; /*!!!!!!!!!!!!!!!不要忘记写!!!!!!!!!!!!!!!*/
ERR5:for (--i; i >= 0; i--) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);
ERR4:cdev_del(cdev);
ERR3:unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:kfree(cdev);
ERR1:return ret;
}
static void __exit mycdev_exit(void)
{int i;for (i = 0; i < count; i++) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);cdev_del(cdev);unregister_chrdev_region(MKDEV(major, minor), count);kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
US:select(maxfd+1,&rfds,NULL,NULL,NULL);
---------------------------------------------------------------
VFS(虚拟文件系统): sys_select1.校验最大文件描述符2.分配6张表的内存(前三张表用来存储用户的文件描述符,后三张表存储准备好的文件描述符)3.将用户空间表拷贝到内存空间4.遍历文件描述符mask = fd_set->fd->fd_array[fd]->file->f_op->poll(file,wait);如果所有的文件描述符对应驱动返回的mask都是0,说明所有的硬件的数据都没有准备好5.进程休眠 (schedule相关的函数)6.如果进程被唤醒(数据准备好,超时时间到,收到了信号),再次遍历文件描述符,找出数据准备好的文件描述符(如果mask不为0,说明数据就准备好了)mask = fd_set->fd->fd_array[fd]->file->f_op->poll(file,wait);将准备好的文件描述符放到准备好的文件描述符的表中7.将准备好的文件描述符拷贝到用户空间
---------------------------------------------------------------
驱动层:
__poll_t mycdev_poll(struct file* file, struct poll_table_struct* wait)
{// 1.定义返回值变量__poll_t mask = 0;// 2.提交等待队列头,并构造等待队列(不会休眠)poll_wait(file, &wq, wait);// 3.判断数据是否准备好if (condition)mask |= EPOLLIN; // EPOLLIN 可读 EPOLLOUT 可写// 4.返回maskreturn mask;
}
select:表
1.最多只能监听1024个文件描述
2.用户空间的表会被清空,需要反复构造文件描述符的表,需要反复从用户空间向内核空间拷贝表效率低
3.当select进程休眠被唤醒之后,需要再次编译文件描述符的表,找出准备好的文件描述符,效率比较低。
poll:结构体数组
1.poll监听的文件描述符没有个数限制
2.poll的表不会被清空,不需要反复拷贝文件描述符,效率比较高。
3.当poll进程休眠被唤醒之后,需要再次编译文件描述符的表,找出准备好的文件描述符,效率比较低。
epoll:红黑树
1.epoll监听的文件描述符没有个数限制
2.epoll的表不会被清空,不需要反复拷贝文件描述符,效率比较高。
3.当epoll进程休眠被唤醒之后,能直接拿到准备好的文件描述符,不需要遍历,效率高。
#include
int epoll_create(int size);
功能:创建epoll
参数:@size:参数已经被忽略了,只需要填写大于0的值即可
返回值:成功返回epfd,失败返回-1置位错误码int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:关于epoll的控制操作
参数:@epfd:epoll的文件描述符@op:控制方式EPOLL_CTL_ADD:添加EPOLL_CTL_MOD:修改EPOLL_CTL_DEL:删除@fd:被操作的文件描述符@event:(事件)结构体指针typedef union epoll_data {void *ptr;int fd; <====一般填写这个成员uint32_t u32;uint64_t u64;} epoll_data_t;struct epoll_event {uint32_t events; //EPOLLIN 读 EPOLLOUT 写epoll_data_t data; //存放用户的数据};
返回值:成功返回0,失败返回-1置位错误码int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:阻塞等待文件描述符就绪
参数:@epfd:epoll的文件描述符@events:准备好的事件的结构体地址@maxevents:返回的最大的文件描述符的个数@timeout:超时>0 :毫秒级别的超时时间=0 :立即返回=-1:不关心超时时间
返回值:成功返回准备好的文件描述符的个数返回0代表超时时间到了失败返回-1置位错误码
代码实现
#include
#include int main(int argc, const char* argv[])
{int epfd, fd, ret;struct epoll_event event;struct epoll_event revents[10];char buf[128] = {0};if ((epfd = epoll_create(10)) == -1)PRINT_ERR("epoll create error");
for (int i = 1; i < argc; i++) {if ((fd = open(argv[i], O_RDWR)) == -1)PRINT_ERR("open error");event.events = EPOLLIN;event.data.fd = fd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event))PRINT_ERR("epoll ctl error");
}while (1) {if ((ret = epoll_wait(epfd, revents, 10, -1)) == -1)PRINT_ERR("epoll wait error");for (int i = 0; i < ret; i++) {if(revents[i].events & EPOLLIN){memset(buf,0,sizeof(buf));read(revents[i].data.fd,buf,sizeof(buf));printf("fd = %d,data = %s\n",revents[i].data.fd,buf);}}
}return 0;
}
异步通知IO模型:应用程序在使用异步通知IO模型的时候需要使用signal(SIGIO,handler)
为SIGIO信号绑定一个信号处理函数。应用程序就可以执行任意自己想要执行的代码。当
硬件的数据准备号之后会产生中断,在中断的处理函数中给这个进程发送信号。当进程
收到信号后执行信号处理函数即可。(信号驱动IO)
异步修饰的是通知而不是IO模型
us:void handle(int signo){//信号处理函数,在信号处理函数中将数据读走即可}//1.注册信号处理函数signal(SIGIO,handle);//2.调用驱动的fasync函数,完成异步通知队列的初始化unsigned int flags=fcntl(fd,F_GETFL);fcntl(fd,F_SETFL,flags | FASYNC);//3.告诉驱动将信号发给当前的进程fcntl(fd,F_SETOWN,getpid());
---------------------------------------------------------
VFS: sys_fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
sys_fcntl(unsigned int fd,unsigned int cmd,unsigned long arg)err = do_fcntl(fd, cmd, arg, f.file);switch (cmd) {case F_GETFL:err = filp->f_flags; break;case F_SETFL:err = setfl(fd, filp, arg);arg = filp->f_flags | FASYNC;if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);}break;}
---------------------------------------------------------
ks:fops:struct fasync_struct *fapp;int (*fasync) (int fd, struct file *filp, int on){ //初始化异步通知队列return fasync_helper(fd,filep,on,&fapp);}//发送信号void kill_fasync(&fapp, SIGIO, POLL_IN);
mycdev.c
#include
#include
#include
#include
#include
#include
#include
#include
#define CNAME "mycdev"
struct cdev* cdev;
unsigned int major = 511;
unsigned int minor = 0;
const int count = 3;
struct class* cls;
struct device* dev;
char kbuf[128] = { 0 };
struct fasync_struct *fapp;
int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
ssize_t mycdev_read(struct file* file,char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);//将数据拷贝到用户空间if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file,const char __user* ubuf, size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data form user error\n");return -EIO;}//发送SIGIO信号kill_fasync(&fapp,SIGIO,POLL_IN);return size;
}int mycdev_fasync(int fd, struct file *filp, int on)
{ //异步通知队列的初始化return fasync_helper(fd,filp,on,&fapp);
}
int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
const struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.fasync = mycdev_fasync,.release = mycdev_close,
};
static int __init mycdev_init(void)
{int ret, i;dev_t devno;// 1.分配对象cdev = cdev_alloc();if (cdev == NULL) {printk("cdev alloc memory error\n");ret = -ENOMEM;goto ERR1;}// 2.初始化对象cdev_init(cdev, &fops);// 3.申请设备号if (major > 0) {//静态指定ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);if (ret) {printk("static:request device number error\n");goto ERR2;}} else {//动态申请ret = alloc_chrdev_region(&devno, 0, count, CNAME);if (ret) {printk("dynamic:request device number error\n");goto ERR2;}major = MAJOR(devno);minor = MINOR(devno);}// 4.注册ret = cdev_add(cdev, MKDEV(major, minor), count);if (ret) {printk("register char device driver error\n");goto ERR3;}// 5.自动创建设备节点cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("class create error\n");ret = PTR_ERR(cls);goto ERR4;}for (i = 0; i < count; i++) {dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);if (IS_ERR(dev)) {printk("device create error\n");ret = PTR_ERR(dev);goto ERR5;}}return 0; /*!!!!!!!!!!!!!!!不要忘记写!!!!!!!!!!!!!!!*/
ERR5:for (--i; i >= 0; i--) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);
ERR4:cdev_del(cdev);
ERR3:unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:kfree(cdev);
ERR1:return ret;
}
static void __exit mycdev_exit(void)
{int i;for (i = 0; i < count; i++) {device_destroy(cls, MKDEV(major, i));}class_destroy(cls);cdev_del(cdev);unregister_chrdev_region(MKDEV(major, minor), count);kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include
#include
int fd;
char buf[128] = { 0 };
void handle(int signo)
{if (signo == SIGIO) {memset(buf, 0, sizeof(buf));read(fd, buf, sizeof(buf));printf("buf = %s\n", buf);}
}
int main(int argc, const char* argv[])
{if ((fd = open("/dev/mycdev0", O_RDWR)) == -1)PRINT_ERR("open error");if ((SIG_ERR == signal(SIGIO, handle)))PRINT_ERR("signal error");fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);fcntl(fd, F_SETOWN, getpid());while (1);close(fd);return 0;
}
上一篇:动态路由协议介绍rip、ospf
下一篇:java-final