应用层的 write 如何调用驱动里的 write 1 编写应用程序测试 hello_dev 驱动 1.1 编写测试程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/select.h> #define DATA_LEN (64) int main (int argc, char *argv[]) { char buf[DATA_LEN] = "Hello, world!" ; int fd = open("/dev/hello" , O_RDWR); if (-1 == fd) { perror("open file error\r\n" ); return -1 ; } printf ("open success\r\n" ); int w_len = write(fd, buf, DATA_LEN); memset (buf, 0 , DATA_LEN); int r_len = read(fd, buf, DATA_LEN); printf ("%d %d\r\n" , w_len, r_len); printf ("%s\r\n" , buf); return 0 ; }
打开 “/dev/hello” 设备,写入数据,然后再读取,控制台输出write、read函数的返回值、缓冲区中的内容
1.2 创建字符设备
1 $ sudo insmod hello_dev.ko
1 2 3 4 5 6 7 MKNOD(1) User Commands MKNOD(1) NAME mknod - make block or character special files SYNOPSIS mknod [OPTION]... NAME TYPE [MAJOR MINOR] DESCRIPTION Create the special file NAME of the given TYPE.
参数
说明
NAME
设备名称
TYPE
设备类型,b表示块设备,c表示字符设备,p表示FIFO
MAJOR
主设备号
MINOR
次设备号
hello_dev 驱动文件中定义主设备号为 232,次设备号为 0,于是创建字符设备,
1 sudo mknod /dev/hello c 232 0
此时执行 ls -l 命令查看设备是否存在,
1 2 $ ls -l /dev/hello crw-r--r-- 1 root root 232, 0 Nov 6 23:52 /dev/hello
1.2 编译、运行测试程序
1 $ gcc -o hello_dev_test hello_dev_test.c
1 2 3 $ ./hello_dev_test open success 0 0
这里 w_len、r_len 为 0,缓冲区为空,因为 hello_dev 驱动源码中,write、read 函数输出内核日志后就直接返回 0 了
1 2 3 4 $ dmesg | tail -3 [ 9040.652155] hello_open [ 9040.652186] hello_write [ 9040.652188] hello_read
hello_dev 驱动程序中定义的 open、write、read 函数确实被调用了
2 从应用层 write 调用到驱动程序中定义的write 不支持在 Docs 外粘贴 block
应用进程调用 c 函数库中定义的 write
c write 函数接着陷入系统调用,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SYSCALL_DEFINE3(write, unsigned int , fd, const char __user *, buf, size_t , count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_write(f.file, buf, count, &pos); if (ret >= 0 ) file_pos_write(f.file, pos); fdput_pos(f); } return ret; }
linux 的系统调用由 SYSCALL_DEFINE 定义
write 系统调用根据文件描述符获取到实际的 file 结构体对象,作为其中1个参数,调用 vfs_write 函数,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ssize_t vfs_write (struct file *file, const char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; ret = rw_verify_area(WRITE, file, pos, count); if (!ret) { if (count > MAX_RW_COUNT) count = MAX_RW_COUNT; file_start_write(file); ret = __vfs_write(file, buf, count, pos); if (ret > 0 ) { fsnotify_modify(file); add_wchar(current, ret); } inc_syscw(current); file_end_write(file); } return ret; }
vfs_write 函数调用底层的 __vfs_write 函数,
1 2 3 4 5 6 7 8 9 ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, loff_t *pos){ if (file->f_op->write) return file->f_op->write(file, p, count, pos); else if (file->f_op->write_iter) return new_sync_write(file, p, count, pos); else return -EINVAL; }
在 __vfs_write 函数中,判断此文件类型是否有定义 write 函数,如果有则直接调用返回结果。file 结构体的 f_op 字段是 file_operations 类型,在 hello_dev 驱动源码中,
1 2 3 4 5 6 7 8 9 10 devNum = MKDEV(reg_major, reg_minor); ............ gDev = kzalloc(sizeof (struct cdev), GFP_KERNEL); gFile = kzalloc(sizeof (struct file_operations), GFP_KERNEL); gFile->open = hello_open; gFile->read = hello_read; gFile->write = hello_write; gFile->owner = THIS_MODULE; cdev_init(gDev, gFile); cdev_add(gDev, devNum, 1 );
将字符设备和 file_operations 结构体对象绑定,然后将字符设备和设备号绑定