文章主要记录了作者在第一次尝试阅读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源码,所以这篇文章会持续更新,记录在阅读源码过程中的一些感悟;

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐