一、文件操作函数
1. 用户空间
查看用户空间文件操作函数说明可在终端:man 2 xxfunc 进行查看
(1)open函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags)
pathname:要打开的设备或者文件名
flags:文件打开模式,以下三种模式必选其一:
O_RDONLY 只读模式
O_WRONLY 只写模式
O_RDWR 读写模式
除了上述三种模式以外还有其他的可选模式,通过逻辑或来选择多种模式:
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件
O_EXCL 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继
I/O 设置为非阻塞
DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提
下,不等待文件属性更新。
O_RSYNC read 等待所有写入同一区域的写操作完成后再进行。
O_SYNC 等待物理 I/O 结束后再 write,包括更新文件属性的 I/O。
返回值:如果文件打开成功的话返回文件的文件描述符
(2)read函数
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count)
fd:要读取的文件描述符。
buf:数据读取到此 buf 中。
count:要读取的数据长度,也就是字节数。
返回值:读取成功的话返回读取到的字节数;如果返回0表示读取到了文件末尾;如果返回负值,表示读取失败。
(3)write函数
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count)
fd:文件描述符。
buf:要写入的数据。
count:要写入的数据长度,也就是字节数。
返回值:写入成功的话返回写入的字节数;如果返回0表示没有写入任何数据;如果返回负值,表示写入失败。
(4)close函数
#include <unistd.h>
int close(int fd);
fd:要关闭的文件描述符。
返回值:0 表示关闭成功,负值表示关闭失败。
(5)ioctl函数
#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...) ;
ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值,如下:
EBADF: fd is not a valid descriptor.
EFAULT: argp references an inaccessible memory area.
EINVAL: Request or argp is not valid.
ENOTTY: fd is not associated with a character special device.
ENOTTY: The specified request does not apply to the kind of object that the descriptor d references.
因此,在用户空间使用 ioctl 时,可以做如下的出错判断以及处理:
int ret;
ret = ioctl(fd, MYCMD);
if (ret == -1) {
printf("ioctl: %s\n", strerror(errno));
}
在实际应用中,ioctl 最常见的 errorno 值为 ENOTTY(error not a typewriter),顾名思义,即第一个参数 fd 指向的不是一个字符设备,不支持 ioctl 操作,这时候应该检查前面的 open 函数是否出错或者设备路径是否正确
2. 内核空间
(1)open函数
static int xxx_open(struct inode *inode, struct file *filp)
(2)read函数
static ssize_t xxx_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
(3)write函数
static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
(4)close函数
static int xxx_release(struct inode *inode, struct file *filp)
(5)ioctl函数
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
具体使用解析见ioctl函数详解
二、内核空间与应用空间得数据交互
1.数据传递函数
(1)copy_from_user
copy_from_user(void *to, const void __user *from, unsigned long n)
@descrption: 将用户空间的数据传递给内核
*to:内核空间的指针
*from:用户空间的指针
n:长度字节数
(2)copy_to_user
copy_to_user(void __user *to, const void *from, unsigned long n)
@descrption: 将内核空间的数据传递给用户
*to:是用户空间的指针
*from:内核空间的指针
n:长度字节数
三、linux内核打印调试等函数
1.内核printk的打印级别
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
2.自定义调试宏
/* 调试宏:通过DEBUG_A进行控制*/
#define DEBUG_A
#ifdef DEBUG_A
#define DEBUG(fmt, args...) printk(KERN_ALERT "DEBUG: " fmt, ## args)
#else
#define DEBUG(fmt, args...)
#endif
3.系统自带终端输出机制以及DEBUG机制
- 终端输出
#define pr_emerg(fmt, ...) \
printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
- DEBUG
/* pr_devel() should produce zero code unless DEBUG is defined */
#ifdef DEBUG
#define pr_devel(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
#include <linux/dynamic_debug.h>
/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
#define pr_debug(fmt, ...) \
dynamic_pr_debug(fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
4.linux内核错误码
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
四、字符设备注册函数
1.设备号注册
(1)alloc_chrdev_region
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
@description:动态注册设备号(未指定设备号)
*dev:存放分配的设备号指针
baseminor:次设备号的起始号
count:要注册设备号的数量
*name:设备名指针
(2)register_chrdev_region
int register_chrdev_region(dev_t from, unsigned count, const char *name)
@description:静态注册设备号(已知设备号)
from:要注册的起始设备号
count:要注册的数量
*name:设备名称指针
(3)unregister_chrdev_region
void unregister_chrdev_region(dev_t from, unsigned count)
@description:注销设备号
from:要注销的起始设备号
count:要注销的数量
(4)一些宏
MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
@description:取主设备号
MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
@description:取次设备号
MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
@description:将主次设备号组合成设备号
2.字符设备注册、节点创建(区别于设备号)
(1)cdev_init
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
@description:初始化cdev结构体。建立操作函数与cdev的联系,cdev就是一个字符设备。
*cdev:字符设备结构体指针
*fops:操作函数结构体指针
(2)cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
@description:向系统添加一个cdev,进而完成字符设备的注册。
*p:cdev指针
dev:设备号
count:设备数量
(3)cdev_del
void cdev_del(struct cdev *p)
@description:在系统中删除一个cdev,进而完成字符设备的注销。
*p:cdev
(4)class_create
struct class *class_create (struct module *owner, const char *name)
@description:创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
*owner:一般为THIS_MODULE
*name:类的名字指针 /sys/class/*name
(5)class_destroy
void class_destroy(struct class *cls);
@description:删除一个类
(6)device_create
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
@descrition:在创建好类之后,在/dev下创建相应的节点。
struct class *class:类
struct device *parent:NULL
dev_t devt :设备号
void *drvdata :null
const char *fmt:名字 /dev/*fmt
(7)device_destroy
void device_destroy(struct class *class, dev_t devt)
@description:删除类与设备节点