Linux5.4.3.1源码阅读
文章主要记录了作者在第一次尝试阅读Linux源码过程中碰到的问题。
为了防止误导读者这里我们进行一个错误的总结:
(1) open函数
首先我们开始想要找到open函数的系统调用实现,于是直接在Linux源码中找到了3个open函数的定义,真正了解后才发现这个是驱动层面的代码实现;后续记录写了如何正确的寻找open函数的系统调用;
(2)kmalloc函数
这里是我在阅读源码过程中的一些方法小技巧,主要是关于如何学习使用内核中提供的API;
一、open函数
阅读open函数的目的是为参看file指针如何存储文件信息的;比如非阻塞标志位等等;
定位到3个位置:
static int open(struct inode *inode, struct file *file)
{
struct controller *ctrl = inode->i_private;
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
mutex_lock(&cpqphp_mutex);
dbg = kmalloc(sizeof(*dbg), GFP_KERNEL);
if (!dbg)
goto exit;
dbg->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!dbg->data) {
kfree(dbg);
goto exit;
}
dbg->size = spew_debug_info(ctrl, dbg->data, MAX_OUTPUT);
file->private_data = dbg;
retval = 0;
exit:
mutex_unlock(&cpqphp_mutex);
return retval;
}
/* tty callbacks */
static int open(struct tty_struct *tty, struct file *filp)
{
struct slgt_info *info;
int retval, line;
unsigned long flags;
line = tty->index;
if (line >= slgt_device_count) {
DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
return -ENODEV;
}
info = slgt_device_list;
while(info && info->line != line)
info = info->next_device;
if (sanity_check(info, tty->name, "open"))
return -ENODEV;
if (info->init_error) {
DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
return -ENODEV;
}
tty->driver_data = info;
info->port.tty = tty;
DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
mutex_lock(&info->port.mutex);
info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
spin_lock_irqsave(&info->netlock, flags);
if (info->netcount) {
retval = -EBUSY;
spin_unlock_irqrestore(&info->netlock, flags);
mutex_unlock(&info->port.mutex);
goto cleanup;
}
info->port.count++;
spin_unlock_irqrestore(&info->netlock, flags);
if (info->port.count == 1) {
/* 1st open on this device, init hardware */
retval = startup(info);
if (retval < 0) {
mutex_unlock(&info->port.mutex);
goto cleanup;
}
}
mutex_unlock(&info->port.mutex);
retval = block_til_ready(tty, filp, info);
if (retval) {
DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
goto cleanup;
}
retval = 0;
cleanup:
if (retval) {
if (tty->count == 1)
info->port.tty = NULL; /* tty layer will release tty struct */
if(info->port.count)
info->port.count--;
}
DBGINFO(("%s open rc=%d\n", info->device_name, retval));
return retval;
}
/* Called when a port is opened. Init and enable port.
*/
static int open(struct tty_struct *tty, struct file *filp)
{
SLMP_INFO *info = tty->driver_data;
unsigned long flags;
int retval;
info->port.tty = tty;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s open(), old ref count = %d\n",
__FILE__,__LINE__,tty->driver->name, info->port.count);
info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
spin_lock_irqsave(&info->netlock, flags);
if (info->netcount) {
retval = -EBUSY;
spin_unlock_irqrestore(&info->netlock, flags);
goto cleanup;
}
info->port.count++;
spin_unlock_irqrestore(&info->netlock, flags);
if (info->port.count == 1) {
/* 1st open on this device, init hardware */
retval = startup(info);
if (retval < 0)
goto cleanup;
}
retval = block_til_ready(tty, filp, info);
if (retval) {
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s block_til_ready() returned %d\n",
__FILE__,__LINE__, info->device_name, retval);
goto cleanup;
}
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s open() success\n",
__FILE__,__LINE__, info->device_name);
retval = 0;
cleanup:
if (retval) {
if (tty->count == 1)
info->port.tty = NULL; /* tty layer will release tty struct */
if(info->port.count)
info->port.count--;
}
return retval;
}
这三个open函数分别属于普通字符设备驱动和TTY子系统驱动;但这三个函数并不是我们要找的open函数的系统调用;在现代Linux操作系统下,一般是通过SYSCALL_DEFINEx或者COMPAT_SYSCALL_DEFINEx这两个宏来定义系统调用,在Source insight软件中,我们可以通过过Ctrl+Shift+F进行全局搜索,这个宏的第一个参数一般是系统调用函数名称,我们直接进入这个宏然后一级一级往里面跳就可以了;
如下是真正我们要寻找的起点,而do_sys_open函数才是真正的执行函数;
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
这里我总结一下Source insight软件的一些快捷键:(我在下面专门设置一个章节写关于Source insight的一些使用技巧)
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
enum rw_hint f_write_hint;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
} __randomize_layout
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
二、kmalloc函数
这里主要是想记录学习API的方法;在初学阶段,我想首先我们需要知道我们要使用哪些API,然后再去内核源码中进行检索,内核源码就是最好的老师;
比如这里我们要学习kmalloc函数,我们直接kmalloc全局搜索,只需要去Ctrl + Shift + F 全局检索关键字,然后随便找一处点进去就可以看到内核源码中如何使用kmalloc了;
kmalloc与kfree函数必须成对出现,这里我们不做过多解释,定义一个字符型指针,接收返回值,判断错误;使用对应的地址内存,最后释放;
static ssize_t
il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char *buff;
int desc = 0;
int j;
ssize_t ret;
struct il3945_rs_sta *lq_sta = file->private_data;
buff = kmalloc(1024, GFP_KERNEL);
if (!buff)
return -ENOMEM;
desc +=
sprintf(buff + desc,
"tx packets=%d last rate idx=%d\n"
"rate=0x%X flush time %d\n", lq_sta->tx_packets,
lq_sta->last_txrate_idx, lq_sta->start_rate,
jiffies_to_msecs(lq_sta->flush_time));
for (j = 0; j < RATE_COUNT_3945; j++) {
desc +=
sprintf(buff + desc, "counter=%d success=%d %%=%d\n",
lq_sta->win[j].counter,
lq_sta->win[j].success_counter,
lq_sta->win[j].success_ratio);
}
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
kfree(buff);
return ret;
}
&&.Source insight使用技巧
Ctrl + Shift + F 全局检索关键字;
Alt + < 返回上一个跳转点;
Alt + > 返回刚刚返回的跳转点;
Ctrl + = 跳转到定义;
当我们Ctrl+F进行文件搜索的时候,可以使用F3(往前)+F4(往后)进行检索;
路漫漫其修远兮,吾将上下而求索;未来大概率需要一直阅读Linux源码,所以这篇文章会持续更新,记录在阅读源码过程中的一些感悟;
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)