Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync

时间:2024-06-10 21:03:14
/***********************************************************************************
*
* Makefile,open,read,write,lseek,poll,ioctl,fasync
*
* 声明:
* 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
* 不对齐,从而影响阅读.
*
*
* 2015-3-9 阴 深圳 尚观 Var 曾剑锋
**********************************************************************************/ \\\\\\\\\\\\\\--*目录*--//////////////
| 一. Makefile大致写法:
| 二. 获取进程task_struct的方法:
| 三. open 大致写法:
| 四. read 大致写法:
| 五. write 大致写法:
| 六. lseek 大致写法:
| 七. poll 大致写法:
| 八. ioctl 大致写法:
| 九. close 大致写法:
| 十. fasync 大致写法:
| 十一. 等待队列API:
| 十二. 驱动wait_queue poll fasync:
| 十三. 应用wait_queue poll fasync:
\\\\\\\\\\\\\\\\\\\/////////////////// 一. Makefile大致写法:
...
# 定义一个CC变量,赋值为arm-linux-
CC = arm-linux-
# .c文件依赖的.h文件存放的目录,这里是:当前目录下include文件夹
INCLUDE_DIRS = include # 如果变量objs在此之前未声明定义,那么就声明定义,并赋值objs为hello.o
# 如果变量objs在此之前已声明定义,那么把hello.o放在objs字符串后面
objs += hello.o # . hello : 下面命令要生成的目标,当然也有可能是伪指令;
# . $(objs) : 依赖文件,只有依赖文件都存在的情况下才会执行下面的指令;
# . $(CC)gcc $< -o $@ : 编译依赖文件产生目标文件;
# . $(CC) : 取CC变量里的值;
# . $< : 依赖文件($(objs))
# . $@ : 目标文件(hello)
hello : $(objs)
$(CC)gcc $< -o $@ # . 生成的目标文件简写,将当前文件下所有的.c编译成对应的.o文件;
# . -I$(INCLUDE_DIRS) : .c编译时需要的头文件所在的目录
%.o : %.c
$(CC)gcc -c $< -o $@ -I$(INCLUDE_DIRS) # 伪命令声明,这样即使存在clean文件,也会把clean当命令使用
.PHONY:clean
clean:
rm *.o hello
... 二. 获取进程task_struct的方法:
...
int ret;
struct thread_info *info;
struct task_struct *task; /**
* 获取当前进程的进程描述符(PCB)算法,主要是因为thread_info里有task_struct指针,
* 而thread_info放在了内核线程栈的起始地方,而内核线程栈是8k对齐,
* 所以thread_finfo的首地址是: ((unsigned int)&ret & ~(0x2000 - 1))
*/
info = (void *)((unsigned int)&ret & ~(0x2000 - ));
task = info->task;
printk("pid = %d, [%s]\n", task->pid, task->comm); /* task->comm 运行的程序名 */ // current 当前进程的PCB指针,系统自带提供的全局变量
printk("pid = %d, [%s]\n", current->pid, current->comm);
... 三. open 大致写法:
. int (*open) (struct inode *, struct file *);
. 代码模型:
static int test_open(struct inode *inode, struct file *file)
{
/**
* 一般open函数里面都要把私有数据绑定到file->private_data上
*/
test_t *p;
p = container_of(file->f_op, test_t, fops);
file->private_data = p; //声明当前设备不支持llseek方法调整读写位置
//nonseekable_open(inode, file);
return ;
} 四. read 大致写法:
. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
. 代码模型:
static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
int ret;
test_t *p = file->private_data; /**
* 检查可读数据是否足够
*/
if(count > BUF_SIZE - *pos)
count = BUF_SIZE - *pos; ret = copy_to_user(buf, p->buf + *pos, count);
if(ret)
return -EFAULT; *pos += count; return count; //返回读到的数据个数
} 五. write 大致写法:
. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
. 代码模型:
static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
test_t *p = file->private_data; if(p->is_empty)
{
/* 进入深度睡眠其中方法中的一种 */
current->state = TASK_UNINTERRUPTIBLE;
schedule();
}
return count;
} 六. lseek 大致写法:
. loff_t (*llseek) (struct file *, loff_t, int);
. .6版本内核不实现该方法的话,使用内核默认的llseek函数,当前版本必须实现该方法;
. 代码模型:
static loff_t test_llseek(struct file *file, loff_t offset, int whence)
{
loff_t pos = file->f_pos; switch(whence)
{
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos += offset;
break;
case SEEK_END:
pos = BUF_SIZE + offset;
break;
default:
return -EINVAL; //参数非法
} file->f_pos = pos; //返回lseek之后的文件读写位置(相对文件开始处)
return pos;
} 七. poll 大致写法:
. poll,epoll,select系统调用的后端,都用作查询对一个或多个文件描述符的读写是否阻塞
. unsigned int (*poll) (struct file *, struct poll_table_struct *);
. 代码模型:
static unsigned int test_poll(struct file *file, struct poll_table_struct *table)
{
unsigned int poll = ;
test_t *p = file->private_data;
/*printk("In test_poll.\n");*/ //功能: 在设备状态变化,等待队列被唤醒时,唤醒select
poll_wait(file, &p->wq, table); //如果可读
if(!p->is_empty)
poll |= POLLIN | POLLRDNORM; return poll;
} 八. ioctl 大致写法:
. long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long);
. cmd的格式:
---------------------------------------
| dir | size | type | nr |
----------------------------------------
. 创建ioctl命令的宏:
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,datatype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(datatype)))
#define _IOW(type,nr,datatype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(datatype)))
#define _IOWR(type,nr,datatype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(datatype)))
. 代码模型:
#define __SET_GET_FOO__
#ifndef __SET_GET_FOO__
#define SET_FOO _IOW(TYPE, NR, int)
#define GET_FOO _IOR(TYPE, NR, int)
#else
#define SET_FOO _IOC(_IOC_WRITE, TYPE, NR, sizeof(int))
#define GET_FOO _IOC(_IOC_READ, TYPE, NR, sizeof(int))
#endif static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret; switch(cmd)
{
case SET_FOO:
ret = copy_from_user(&num, (void *)arg, sizeof(int));
if(ret)
return -EFAULT;
break;
case GET_FOO:
ret = copy_to_user((void *)arg, &num, sizeof(int));
if(ret)
return -EFAULT;
break;
default:
return -EINVAL;
} return ;
} 九. close 大致写法:
. int (*release) (struct inode *, struct file *);
. 代码模型:
static int test_close(struct inode *inode, struct file *file)
{
return ;
} 十. fasync 大致写法:
. int (*fasync) (int, struct file *, int);
. 前提条件:
. 应用层(app)设置fd的owner: fcntl(fd, F_SETOWN, getpid()); //设置fd的owner
. fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); //设置O_ASYNC标志
. 当使用fcntl()设置O_ASYNC时,会调用fasync()函数.
. 代码模型:
static int test_fasync(int fd, struct file *file, int on)
{
test_t *p = file->private_data;
printk("In test_fasync: fd = %d, on = %d\n", fd, on); // fasync_helper自己会去申请结构体struct fasync_struct的空间,并赋值
// 所以在这里我们只需要传入指向这个结构体的指针就行了
return fasync_helper(fd, file, on, &p->fa);
}
. 在设备状态变化时发送信号给使用异步通知的进程: kill_fasync(&p->fa, SIGIO, POLL_IN); 十一. 等待队列API:
. 定义: wait_queue_head_t wq;
. 初始化: init_waitqueue_head(&wq);
. 两种把当前进程加入到等待队列睡眠:
. wait_event(wq, cond); //深度睡眠,top命令下状态标志: D
. wait_event_interruptible(wq, cond); //可被信号中断睡眠,top命令下状态标志: S
. 两种唤醒等待队列:
. wake_up(&wq); //唤醒深度睡眠,普通睡眠:D,S
. wake_up_interruptible(&wq); //唤醒普通睡眠:S 十二. 驱动wait_queue poll fasync:
cat > test.c << EOF
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/poll.h> //设备名,可以通过命令: cat /proc/devices
#define DEV_NAME "test" struct test_s {
struct file_operations fops;
wait_queue_head_t wq;
struct fasync_struct *fa;
int major;
bool is_empty;
};
typedef struct test_s test_t; static int test_open(struct inode *inode, struct file *file)
{
//把驱动私有数据放到file的private_data中
test_t *p;
p = container_of(file->f_op, test_t, fops);
file->private_data = p; return ;
} static int test_close(struct inode *inode, struct file *file)
{
return ;
} static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
test_t *p = file->private_data; /**
* 修正能够输出的数据长度,在本Demo中没有用到.
* if(count > BUF_SIZE - *pos)
* count = BUF_SIZE - *pos;
*/ while(p->is_empty)
{
//判断当前打开文件方式是不是非阻塞方式打开的
if(file->f_flags & O_NONBLOCK)
return -EAGAIN; //如果没有数据,那么就进入睡眠状态,等待被唤醒,同时要求条件为真: !p->is_empty
if(wait_event_interruptible(p->wq, !p->is_empty))
return -ERESTARTSYS;
} printk("Read data.\n"); p->is_empty = true; //数据读完,重新置为空 return count; //返回数据的个数
} /**
* 调用这个方法请使用命令: echo 123456 > /dev/test0
*/
static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
test_t *p = file->private_data; p->is_empty = false; //将状态置为有数据
printk("Write data.\n"); /**
* 因为下面的kill_fasync()能唤醒可中断睡眠,所以可以不需要这行代码
*/
//wake_up_interruptible(&p->wq); //唤醒等待队列中的进程 kill_fasync(&p->fa, SIGIO, POLL_IN); //发送SIGIO信号量,并且表示数据可读 return count;
} static unsigned int test_poll(struct file *file, struct poll_table_struct *table)
{
unsigned int poll = ;
test_t *p = file->private_data;
/*printk("In test_poll.\n");*/ /**
* 功能: 在设备状态变化,等待队列被唤醒时,唤醒select
* poll()可被调用多次,也导致poll_wait()被注册多次,不过没事,正常现象
*/
poll_wait(file, &p->wq, table); //如果可读,将状态返回
if(!p->is_empty)
poll |= POLLIN | POLLRDNORM; return poll;
} static int test_fasync(int fd, struct file *file, int on)
{
test_t *p = file->private_data;
printk("In test_fasync: fd = %d, on = %d\n", fd, on); //注册异步事件
return fasync_helper(fd, file, on, &p->fa);
} struct test_s test = {
.fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.read = test_read,
.write = test_write,
.poll = test_poll,
.fasync = test_fasync,
},
.major = ,
.is_empty = true,
}; int __init test_init(void)
{
int ret; // 初始化等待队列
init_waitqueue_head(&test.wq); // 注册字符设备驱动
ret = register_chrdev(test.major, DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
} void __exit test_exit(void)
{
// 卸载字符设备驱动
unregister_chrdev(test.major, DEV_NAME);
} module_init(test_init);
module_exit(test_exit); MODULE_LICENSE("GPL");
EOF 十三. 应用wait_queue poll fasync:
cat > app.c << EOF
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h> int fd; void sig_handler(int sig)
{
int ret;
char buf[];
if(sig == SIGIO)
printf("got a signal SIGIO\n"); /**
* ret = read(fd, buf, sizeof(buf));
* if(-1 == ret)
* perror("read");
*/
} int main(int argc, char **argv)
{
int ret;
fd_set fds;
char buf[] = "hello"; signal(SIGIO, sig_handler); fd = open(argv[], O_RDWR);
if(- == fd)
{
perror("open");
exit();
} fcntl(fd, F_SETOWN, getpid()); //设置fd的owner
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); //设置O_ASYNC标志 while()
{
ret = read(fd, buf, );
if (- == ret ) {
perror("read");
}
} close(fd);
return ;
}
EOF