-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
1,159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
// THIS_MODULE [@include/linux/export.h] | ||
extern struct module __this_module; | ||
#define THIS_MODULE (&__this_module) | ||
|
||
// __this_module [@scripts/mod/modpost.c] | ||
static void add_header(struct buffer *b, struct module *mod) | ||
{ | ||
buf_printf(b, "#include <linux/module.h>\n"); | ||
buf_printf(b, "#include <linux/vermagic.h>\n"); | ||
buf_printf(b, "#include <linux/compiler.h>\n"); | ||
buf_printf(b, "\n"); | ||
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); | ||
buf_printf(b, "\n"); | ||
buf_printf(b, "struct module __this_module\n"); | ||
buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n"); | ||
buf_printf(b, "\t.name = KBUILD_MODNAME,\n"); | ||
if (mod->has_init) | ||
buf_printf(b, "\t.init = init_module,\n"); | ||
if (mod->has_cleanup) | ||
buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" | ||
"\t.exit = cleanup_module,\n" | ||
"#endif\n"); | ||
buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); | ||
buf_printf(b, "};\n"); | ||
} | ||
|
||
// kernel用struct module列表来代表各种module | ||
// 由此创建了 xxx.mod.c 文件, 但是module name还是没有找到, | ||
|
||
// KBUILD_MODNAME 创建方式如下: [@Makefile] | ||
// -DMACRO=DEFN 以字符串“DEFN”定义 MACRO 宏 | ||
gcc -DKBUILD_MODNAME=demo_module ...... | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* include/linux/err.h | ||
* ERR_PTR, PTR_ERR, IS_ERR etc. | ||
*/ | ||
|
||
// kernel 中关于这些error宏的解释 | ||
|
||
// 最大错误码为 4K - 1,也就是说 -MAX_ERRNO到-1,每一个都表示一种错误 | ||
#define MAX_ERRNO 4095 | ||
|
||
// ERR_PTR宏可以这样理解: change "error number" to "pointer" | ||
// 参数long error是错误码,强制类型转换为(void *),便成了内存地址指针,如: | ||
// error == -4095,其对应的补码表示为 0xFFFFF001 | ||
// 强制类型转换为(void *),0xFFFFF001便成了一个地址 | ||
static inline void * __must_check ERR_PTR(long error) | ||
{ | ||
return (void *) error; | ||
} | ||
|
||
// PTR_ERR宏理解为:change "pointer" to "error number" | ||
// 跟ERR_PTR宏正好相反 | ||
static inline long __must_check PTR_ERR(const void *ptr) | ||
{ | ||
return (long) ptr; | ||
} | ||
|
||
// IS_ERR宏的原理是这样的: | ||
// 内核分配的内存都是以page为单位的,在32位系统上,一个page为4K大小, | ||
// 但是最后一个page是禁止使用的,看高端内存可知,这最后一个page是作为 | ||
// fixed_map使用的,像驱动程序等是无法分配到这个page的,所以如果分配的 | ||
// 内存指针跑到最后一个page地址范围(0xFFFFF000 ~ 0xFFFFFFFF)了,那么 | ||
// 这个一定是发生错误了。 | ||
static inline long __must_check IS_ERR(const void *ptr) | ||
{ | ||
return IS_ERR_VALUE((unsigned long)ptr); | ||
} | ||
|
||
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) | ||
// 其中(unsighed long)-MAX_ERRNO的十六进制表示为:0xFFFFF001,恰好比最后一个 | ||
// page的地址(0xFFFFF000)大 1. | ||
// 可以联合这些宏来使用: | ||
// 如果IS_ERR宏返回true,就调用PTR_ERR宏得到错误码。 | ||
// 这就是PTR_RET宏的实现原理: | ||
static inline int __must_check PTR_RET(const void *ptr) | ||
{ | ||
if (IS_ERR(ptr)) | ||
return PTR_ERR(ptr); | ||
else | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* | ||
* | ||
* Linux open operation: from userspace to driver. | ||
* | ||
*/ | ||
|
||
// Part One | ||
// 0. system call entrance [@fs/open.c] | ||
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) | ||
{ | ||
long ret; | ||
|
||
if (force_o_largefile()) | ||
flags |= O_LARGEFILE; | ||
//** this is the main operation | ||
ret = do_sys_open(AT_FDCWD, filename, flags, mode); | ||
/* avoid REGPARM breakage on x86: */ | ||
asmlinkage_protect(3, ret, filename, flags, mode); | ||
return ret; | ||
} | ||
|
||
// 1. do_sys_open [@fs/open.c] | ||
long do_sys_open(int dfd, const char __user *filename, int flags, int mode) | ||
{ | ||
char *tmp = getname(filename); | ||
int fd = PTR_ERR(tmp); | ||
|
||
if (!IS_ERR(tmp)) { | ||
fd = get_unused_fd_flags(flags); | ||
if (fd >= 0) { | ||
//** this is the main operation | ||
struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); | ||
if (IS_ERR(f)) { | ||
put_unused_fd(fd); | ||
fd = PTR_ERR(f); | ||
} else { | ||
fsnotify_open(f->f_path.dentry); | ||
fd_install(fd, f); | ||
} | ||
} | ||
putname(tmp); | ||
} | ||
return fd; | ||
} | ||
|
||
// 2. do_filp_open | ||
struct file *do_filp_open(int dfd, const char *pathname, | ||
int open_flag, int mode, int acc_mode) | ||
{ | ||
struct file *filp; | ||
struct nameidata nd; | ||
// ...... | ||
filp = get_empty_filp(); | ||
if (filp == NULL) | ||
goto exit_parent; | ||
nd.intent.open.file = filp; | ||
// ...... | ||
filp = nameidata_to_filp(nd); | ||
// ...... | ||
} | ||
|
||
// 3. nameidata_to_filp | ||
struct file *nameidata_to_filp(struct nameidata *nd) | ||
{ | ||
const struct cred *cred = current_cred(); | ||
struct file *filp; | ||
|
||
/* Pick up the filp from the open intent */ | ||
filp = nd->intent.open.file; | ||
/* Has the filesystem initialised the file for us? */ | ||
if (filp->f_path.dentry == NULL) | ||
// this is the main operation | ||
filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp, NULL, cred); | ||
else | ||
path_put(&nd->path); | ||
return filp; | ||
} | ||
|
||
// 4. __dentry_open | ||
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | ||
struct file *f, | ||
int (*open)(struct inode *, struct file *), | ||
const struct cred *cred) | ||
{ | ||
struct inode *inode; | ||
int error; | ||
|
||
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; | ||
inode = dentry->d_inode; | ||
// ...... | ||
f->f_mapping = inode->i_mapping; | ||
f->f_path.dentry = dentry; | ||
f->f_path.mnt = mnt; | ||
f->f_pos = 0; | ||
//** this is the main operation | ||
f->f_op = fops_get(inode->i_fop); | ||
file_move(f, &inode->i_sb->s_files); | ||
// ...... | ||
//** this is the main operation | ||
if (!open && f->f_op) | ||
open = f->f_op->open; | ||
if (open) { | ||
error = open(inode, f); | ||
} | ||
} | ||
|
||
//********** 现在我们知道: struct file中的 f->f_op就是struct inode中的inode->i_fop,阶段性胜利! **************// | ||
// Part Two | ||
// 下面来看inode->i_fop又是如何关联到driver中的内容的 | ||
|
||
// 0. init_special_inode , i_rdev 表示设备号 [@fs/inode.c] | ||
// 手动执行命令mknode时会调用到mknode函数(比如ext3_mknode()),然后调用到init_special_inode()函数 | ||
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) | ||
{ | ||
inode->i_mode = mode; | ||
if (S_ISCHR(mode)) { | ||
inode->i_fop = &def_chr_fops; | ||
inode->i_rdev = rdev; | ||
} else if (S_ISBLK(mode)) { | ||
inode->i_fop = &def_blk_fops; | ||
inode->i_rdev = rdev; | ||
} else if (S_ISFIFO(mode)) | ||
inode->i_fop = &def_fifo_fops; | ||
else if (S_ISSOCK(mode)) | ||
inode->i_fop = &bad_sock_fops; | ||
else | ||
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for" | ||
" inode %s:%lu\n", mode, inode->i_sb->s_id, | ||
inode->i_ino); | ||
} | ||
|
||
// def_chr_fops [@fs/char_dev.c] | ||
const struct file_operations def_chr_fops = { | ||
.open = chrdev_open, | ||
}; | ||
|
||
// chrdev_open | ||
/* | ||
* Called every time a character special file is opened | ||
*/ | ||
static int chrdev_open(struct inode *inode, struct file *filp) | ||
{ | ||
struct cdev *p; | ||
struct cdev *new = NULL; | ||
int ret = 0; | ||
|
||
spin_lock(&cdev_lock); | ||
p = inode->i_cdev; | ||
if (!p) { | ||
struct kobject *kobj; | ||
int idx; | ||
spin_unlock(&cdev_lock); | ||
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); | ||
if (!kobj) | ||
return -ENXIO; | ||
// new即是我们的driver结构对应的struct cdev结构 | ||
new = container_of(kobj, struct cdev, kobj); | ||
spin_lock(&cdev_lock); | ||
/* Check i_cdev again in case somebody beat us to it while | ||
we dropped the lock. */ | ||
p = inode->i_cdev; | ||
// 第一次打开时,p==NULL | ||
if (!p) { | ||
inode->i_cdev = p = new; | ||
list_add(&inode->i_devices, &p->list); | ||
new = NULL; | ||
} else if (!cdev_get(p)) | ||
ret = -ENXIO; | ||
} else if (!cdev_get(p)) | ||
ret = -ENXIO; | ||
spin_unlock(&cdev_lock); | ||
cdev_put(new); | ||
if (ret) | ||
return ret; | ||
|
||
ret = -ENXIO; | ||
// 现在将我们的driver结构对应的struct cdev结构中的fops赋值给filp->f_op | ||
filp->f_op = fops_get(p->ops); | ||
if (!filp->f_op) | ||
goto out_cdev_put; | ||
|
||
// 如果我们实现open()的话,就调用它,注意open的参数哦,尤其是那个filp. | ||
if (filp->f_op->open) { | ||
ret = filp->f_op->open(inode,filp); | ||
if (ret) | ||
goto out_cdev_put; | ||
} | ||
|
||
return 0; | ||
|
||
out_cdev_put: | ||
cdev_put(p); | ||
return ret; | ||
} | ||
|
||
// 通过两部分上下结合,我们就知道:驱动中的file operations是如何赋值到struct file的f_op中去了。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
|
||
// 0. module_init macro [@include/linux/init.h] | ||
#define module_init(x) __initcall(x); | ||
|
||
// 1. __initcall macro [@include/linux/init.h] | ||
#define __initcall(fn) device_initcall(fn) | ||
|
||
// 2. device_initcall macro [@include/linux/init.h] | ||
#define device_initcall(fn) __define_initcall("6",fn,6) | ||
|
||
// 3. __define_initcall macro [@include/linux/init.h] | ||
// __attribute__((__section__("section-name"))) 表示 | ||
// 把符号放置到"section-name"段 | ||
// __used 表示该符号必须有定义, 没音定义的话编译器就会报错, | ||
// __unused 表示符号定义可有可无, 没有定义的话编译器不会报错 | ||
// 具体到这里的解释是(以vivi_init函数为例): | ||
// static initcall_t __initcall_vivi_init6 = vivi_init; | ||
// 符号 __initcall_vivi_init6 必须要有定义, 否则编译器报错, | ||
// 而且该符号要放置到 .initcall6.init 段中. | ||
#define __define_initcall(level,fn,id) \ | ||
static initcall_t __initcall_##fn##id __used \ | ||
__attribute__((__section__(".initcall" level ".init"))) = fn | ||
|
Oops, something went wrong.