一、linux系统将设备分为3类:字符设备、块设备、网络设备。
- 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。
- 块设备:是指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。
- 网络设备是计算机体系结构中必不可少的一部分,处理器如果想与外界通信,通常都会选择网络设备作为通信接口。众所周知,在 OSI(Open Systems
Interconnection,开放网际互连)中,网络被划分为七个层次,从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。我们所讲的网络设备也包括两个层次,一层叫做
MAC(Media Access Control)层,对应于 OSI 的数据链路层;另一层叫做 PHY(Physical Layer)层,对应于物理层。
1.cdev结构体
struct cdev{
struct kobject kobj;//内嵌的kobj对象
struct module *owner;//所属模块
struct file_operations *ops;//文件操作结构体
struct list_head list;
dev_t dev;//设备号
unsigned int count;
};
cdev 结构体的dev_t成员定义了设备号,为32位,其中高12位为主设备号,低20位为次设备号.
MAJOR(dev_t dev)获得主设备号和MINOR(dev_t dev)获得次设备号
MKDEV(int major,int minor)可以生成dev_t
file_operation定义了字符设备提供给文件系统的接口函数
void cedv_init (struct cdev *,struct file_operation *);//初始化cedv的成员,建立cedv和file_operation的连接
struct cdev *cdev_alloc(void);//动态申请一个cdev内存
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
//cdev_add()函数和 cdev_del()函数分别向系统添加和删除一个 cdev, 完成字符设备的注册和注销
cdev_add()向系统注册字符设备时,先调用register_chrdev_region()或者alloc_chrdev_region()向系统申请设备号
cdev_del()向系统注销字符设备后,unregister_chrdev_region()应该被调用以释放原先申请的设备号
分配和释放设备号
int register_chrdev_region(dev_t from,unsigned count,const char* name);//用已知的设备号
int alloc_chrdev_region(dev_t *dev ,unsigned baseminor,unsigned count,const char *name);//用于设备号未知
void unregister_chrdev_region(dev_t from,unsigned count);//卸载
file_operations结构体
struct file_operation{
struct module *owner;//拥有该模块的指针,一般为this module
loff_t(*llseek)(struct file *,loff_t ,int ,);//用来修改文件的当前位置,并将位置返回,出错返回负值
ssize_t(*read)(struct file * ,char user *,size_t,loff_t);//同步读取,成功返回内容,失败负值
ssize_t(*aio_read)(struct kiocb *,char user *,size_t ,loff_t);//异步读取
ssize_t(*write)(struct file * ,char user *,size_t,loff_t);//同步写入,成功写入内容,失败负值
int(*readdir)(struct file *,void *,filldir_t);//读取目录,对于设备文件,该字段为空,设备节点不需要实现它
// 不使用 BLK 文件系统,将使用此种函数指针代替 ioctl
long(*compat_ioctl)(struct file *, unsigned int, unsigned long);
// 在 64 位系统上,32 位的 ioctl 调用将使用此函数指针代替
int(*mmap)(struct file *, struct vm_area_struct*);
// 用于请求将设备内存映射到进程地址空间,如果设备驱动未实现此函数,用户进行 mmap()系统调用时将获得-ENODEV 返回值
int(*open)(struct inode *, struct file*);
// 打开,驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功。
int(*flush)(struct file*);
int(*release)(struct inode *, struct file*);
// 关闭
int(*synch)(struct file *, struct dentry *, int datasync);
// 刷新待处理的数据
int(*aio_fsync)(struct kiocb *, int datasync);
// 异步 fsync
int(*fasync)(int, struct file *, int);
// 通知设备 FASYNC 标志发生变化
int(*lock)(struct file *, int, struct file_lock*);
ssize_t(*readv)(struct file *, const struct iovec *, unsigned long,loff_t*);
ssize_t(*writev)(struct file *, const struct iovec *, unsigned long,loff_t*);
// readv 和 writev:分散/聚集型的读写操作
ssize_t(*sendfile)(struct file *, loff_t *, size_t, read_actor_t,void*);
// 通常为 NULL
ssize_t(*sendpage)(struct file *, struct page *, int, size_t,loff_t *, int);
// 通常为 NULL
unsigned long(*get_unmapped_area)(struct file *,unsigned long,unsigned long,unsigned long, unsigned long);
// 在进程地址空间找到一个将底层设备中的内存段映射的位置
int(*check_flags)(int);
// 允许模块检查传递给 fcntl(F_SETEL...)调用的标志
int(*dir_notify)(struct file *filp, unsigned long arg);
// 仅对文件系统有效,驱动程序不必实现
int(*flock)(struct file *, int, struct file_lock*);
};
unsigned long copy_from_user(void *to,const void User *from,unsigned long count);
unsigned long copy_to_user(void user* to,const void *from,unsigned count );
//user是一个宏,表明其后的指针指向用户空间
get_user(val,(int *)arg);//用户空间到内核空间,arg是用户空间的地址
put_user(val,(int *)arg);////内核空间到用户空间,arg 是用户空间的地址