应用层:
open时由O_NONBLOCK指示read、write时是否阻塞
open以后可以由fcntl函数来改变是否阻塞:
flags = fcntl(fd,F_GETFL,0); flags |= O_NONBLOCK; fcntl(fd, F_SETFL, flags);
驱动层:通过等待队列
wait_queue_head_t //等待队列头数据类型 init_waitqueue_head(wait_queue_head_t *pwq) //初始化等待队列头 wait_event_interruptible(wq,condition)//浅度睡眠,条件不成立,则进入等待队列 /* 功能:条件不成立则让任务进入浅度睡眠,直到条件成立醒来 wq:等待队列头 condition:C语言表达式 返回:正常唤醒返回0,信号唤醒返回非0(此时读写操作函数应返回-ERESTARTSYS) */ wait_event(wq,condition) //深度睡眠 wake_up_interruptible(wait_queue_head_t *pwq)//唤醒浅度睡眠 wake_up(wait_queue_head_t *pwq)//唤醒深度睡眠 /* 1. 读、写用不同的等待队列头rq、wq 2. 无数据可读、可写时调用wait_event_interruptible(rq、wq,条件) 3. 写入数据成功时唤醒rq,读出数据成功唤醒 */
2.在init函数里面,初始化这两个等待队列
linux@linux:~/fs4412/mydrivercode$ lsmod | grep char
linux@linux:~/fs4412/mydrivercode$ sudo insmod ./mychar.ko
[sudo] password for linux:
linux@linux:~/fs4412/mydrivercode$ cat /proc/devices |grep cahr
linux@linux:~/fs4412/mydrivercode$ cat /proc/devices |grep char
11 mychar
linux@linux:~/fs4412/mydrivercode$ sudo mknod /dev/mydev c 11 0
linux@linux:~/fs4412/mydrivercode$ ls /dev/mydev* -l
crw-r--r-- 1 root root 11, 0 Jan 18 00:19 /dev/mydev
linux@linux:~/fs4412/mydrivercode$ sudo chmod a+w /dev/mydev
linux@linux:~/fs4412/mydrivercode$ ls /dev/mydev* -l
crw-rw-rw- 1 root root 11, 0 Jan 18 00:19 /dev/mydev
linux@linux:~/fs4412/mydrivercode$ vi testmychar_app.c
linux@linux:~/fs4412/mydrivercode$ cp testmychar_app.c testmychar_nonblockread.c
linux@linux:~/fs4412/mydrivercode$ vi testmychar_nonblockread.c
linux@linux:~/fs4412/mydrivercode$ gcc testmychar_nonblockread.c -o trn -Wall
linux@linux:~/fs4412/mydrivercode$ ./trn /dev/mydev
read data failed
#include
#include
#include
#include
#include
#include
#include
#include "mychar.h"
#define BUF_LEN 100
int major = 11;//主设备号
int minor =0;//次设备号
int mychar_num = 1;//次设备数量
//ioctrl
struct mychar_dev
{
struct cdev mydev;
char mydev_buf[BUF_LEN];
int curlen;//100个字节中已经存有的数据
wait_queue_head_t rq;
wait_queue_head_t wq;
};
struct mychar_dev gmydev;
int mychar_open(struct inode *pnode,struct file *pfile)
{//inode * pnode 中有一个成员 i_rdev存放这mydev的地址,现在用struct成员的地址求出结构体的地址
//求出gmydev的地址
pfile->private_data = (void *)container_of(pnode->i_cdev,struct mychar_dev,mydev);
printk("mychar_open is called\n");
return 0;
}
int mychar_close(struct inode *pnode,struct file *pfile)
{
printk("mychar_close is called\n");
return 0;
}
ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{//内核空间mydev_buf 向 用户空间puser 进行copy
struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
//定义一个指针pmydev
int size = 0;
int ret = 0;
if(pmydev->curlen<=0)//如果无数据可读
{
if(pfile->f_flags & O_NONBLOCK)//如果为真,fd描述符为非阻塞
{//非阻塞,且无数据可读,返回错误
printk("O_NONBLOCK no data to read\n");
return -1;
}
else
{//阻塞,且无数据可读,加入到等待队列中去,进入浅度睡眠中
ret = wait_event_interruptible(pmydev->rq,pmydev->curlen > 0);
if(ret)//if ret==0,条件成立,正常唤醒,下面语句不执行
{
printk("wake up by signal\n");
return -ERESTART;
}
}
}
if(count >pmydev->curlen)//如果期望读取的数据大小大于了原本数据大小
{
size = pmydev->curlen;//读取的数据为被读取数据的大小
}
else
{
size = count;//被读取的数据大小为期望读取的数据大小
}
ret = copy_to_user(puser,pmydev->mydev_buf,size);
if(ret)//
{
printk("copy_to_user failed\n");
return -1;
}
//将没被读取的数据拷贝到初始读取的位置.
memcpy(pmydev->mydev_buf,pmydev->mydev_buf + size,pmydev->curlen - size);
pmydev->curlen =pmydev-> curlen -size;//被读取后剩余这么多字节
//一旦读取一些数据后,curlen一定小于BUF_LEN,直接唤醒write函数中的写队列
wake_up_interruptible(&pmydev->wq);
return size;
}
ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos)
{//用户空间puser 向 内核空间mydev_buf copy
int ret=0;
int size = 0;
struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
//阻塞与非阻塞
if(pmydev->curlen>=BUF_LEN)//如果空间可写
{
if(pfile->f_flags & O_NONBLOCK)//如果为真,fd描述符为非阻塞
{//非阻塞,且无数据可读,返回错误
printk("O_NONBLOCK no space to write\n");
return -1;
}
else
{//阻塞,且无空间可写,加入到等待队列中去,进入浅度睡眠中
ret = wait_event_interruptible(pmydev->wq,pmydev->curlenBUF_LEN - pmydev->curlen)//如果期望写入的数据大小大于100个字节剩余的空间
{
size = BUF_LEN - pmydev->curlen;
}
else
{
size = count;
}
ret = copy_from_user(pmydev->mydev_buf,puser,size);
//将用户空间puser中 size大小的数据写入到内核空间mydev_buf为始的地址中去
if(ret)
{
printk("copy_from_user is failed\n");
return -1;
}
pmydev->curlen = pmydev->curlen +size;
//mydev_buf中存在的数据大小
//一旦写入数据后,curlen一定大于0,直接唤醒read函数中的睡眠队列
wake_up_interruptible(&pmydev->rq);
return size;
}
//实现ioctl
long mychar_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{
int __user *pret = (int *)arg;
int maxlen = BUF_LEN;
int ret = 0;
struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
switch(cmd)
{
case MYCHAR_IOCTL_GET_MAXLEN:
//把最大值copy到用户空间去
ret = copy_to_user(pret,&maxlen,sizeof(int));
if(ret)
{
printk("copy_to_user maxlen failed\n ");
return -1;
}
break;
case MYCHAR_IOCTL_GET_CURLEN:
//把当前值copy到用户空间去
ret = copy_to_user(pret,&pmydev->curlen,sizeof(int));
if(ret)
{
printk("copy_to_user curlen failed\n ");
return -1;
}
break;
default:
printk("the cmd is unknow\n");
return -1;
}
return 0;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = mychar_open,
.release = mychar_close,
.read = mychar_read,
.write = mychar_write,
.unlocked_ioctl = mychar_ioctl,
};
int __init mychar_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major,minor);//组合成完整的设备号
/*申请设备号*/
ret = register_chrdev_region(devno,mychar_num,"mychar");
if(ret)//ret非0,表示失败
{
ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
//此设备号申请后填写到devno地址中去,从minor开始申请mychar_num个
if(ret)
{
printk("get devno failed\n");
return -1;
}
major = MAJOR(devno);//获取新的设备号,不要遗漏
//次设备号都是0,所以不用再次提取
}
//给struct_cdev对象制定操作函数集
cdev_init(&gmydev.mydev,&myops);
//将struct_cdev对象添加到内核对应的数据结构里
gmydev.mydev.owner = THIS_MODULE;
cdev_add(&gmydev.mydev,devno,1);
init_waitqueue_head(&gmydev.rq);
init_waitqueue_head(&gmydev.wq);
return 0;
}
void __exit mychar_exit(void)
{
dev_t devno = MKDEV(major,minor);
cdev_del(&gmydev.mydev);
unregister_chrdev_region(devno,mychar_num);
}
MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);
#include
#include
#include
#include
#include
#include
#include "mychar.h"
int main(int argc, const char *argv[])
{
int fd = -1;
char buf[8] = "";
int ret = 0;
if(argc<2)
{
printf("the argument is too few\n");
return 1;
}
//设为非阻塞,阻塞则去掉 |
fd = open(argv[1],O_RDWR|O_NONBLOCK);
if(fd<0)
{
printf("open %s failed\n",argv[1]);
return 2;:
}
ret= read(fd,buf,8);
if(ret<0)
{
printf("read data failed\n");
}
else
{
printf("buf = %s\n",buf);
}
close(fd);
fd = -1 ;
return 0;
}