祝各位道友念头通达
GitHub Gitee 语雀 打赏

Centos 下配置linux源码阅读

环境

[root@linux-3.10.0-1160.49.1.el7.x86_64#] cat /etc/redhat-release 
CentOS Linux release 7.9.2009 (Core)
[root@linux-3.10.0-1160.49.1.el7.x86_64#] uname -a
Linux VM-24-7-centos 3.10.0-1160.49.1.el7.x86_64 #1 SMP Tue Nov 30 15:51:32 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

对应的源码rpm安装包: https://vault.centos.org/7.9.2009/updates/Source/SPackages/kernel-3.10.0-1160.49.1.el7.src.rpm

源码安装教程

参考 https://wiki.centos.org/HowTos/I_need_the_Kernel_Source
按照下步逐步执行

yum install -y kernel-devel
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros
yum install -y asciidoc audit-libs-devel bash bc binutils binutils-devel bison diffutils elfutils
yum install -y elfutils-devel elfutils-libelf-devel findutils flex gawk gcc gettext gzip hmaccalc hostname java-devel
yum install -y m4 make module-init-tools ncurses-devel net-tools newt-devel numactl-devel openssl
yum install -y patch pciutils-devel perl perl-ExtUtils-Embed pesign python-devel python-docutils redhat-rpm-config
yum install -y rpm-build sh-utils tar xmlto xz zlib-devel
rpm -ivp https://vault.centos.org/7.9.2009/updates/Source/SPackages/kernel-3.10.0-1160.49.1.el7.src.rpm

#如果网络延迟,可以先将rpm下载下来, 直接用 rpm -ivp kernel-3.10.0-1160.49.1.el7.src.rpm 即可
#如果再出错的话 warning: group mockbuild does not exist - using root, 执行以下两个指令
yum install mock
useradd -s /sbin/nologin mockbuild

cd ~/rpmbuild/SPECS
rpmbuild -bp --target=$(uname -m) kernel.spec
# 查看源码的目录
cd ~/rpmbuild/BUILD/kernel*/linux*/

安装完之后

[root@linux-3.10.0-1160.49.1.el7.x86_64#] pwd
/root/rpmbuild/BUILD/kernel-3.10.0-1160.49.1.el7/linux-3.10.0-1160.49.1.el7.x86_64
[root@linux-3.10.0-1160.49.1.el7.x86_64#] ls
arch     COPYING  Documentation  fs       ipc      kernel       Makefile        net             samples   sound  virt
block    CREDITS  drivers        include  Kbuild   lib          Makefile.qlock  README          scripts   tools
configs  crypto   firmware       init     Kconfig  MAINTAINERS  mm              REPORTING-BUGS  security  usr

使用vscode远程到目录 查看内核代码

如图:
image

linux 内核函数接口如何提供给用户态

  1. 内核态的函数接口不是直接提供给用户态调用的, 比如用户态调用 msgsnd 函数
  2. 该函数是通过 msg.h 提供的
extern int msgsnd (int __msqid, const void *__msgp, size_t __msgsz,
		   int __msgflg);
  1. 在内核代码中, sys_msgsnd, 对系统API函数相对内核的API可以查看内核文件 ./tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
  2. 在内核的代码接口声明为
asmlinkage long sys_msgsnd(int msqid, struct msgbuf __user *msgp,
				size_t msgsz, int msgflg);

asmlinkage 参数的说明: 能够正确实现C++代码调用其他C语言代码
参考:https://blog.csdn.net/Ivan804638781/article/details/111293021
__user 这个特性,即__attribute__((noderef, address_space(1))),是用来修饰一个变量的,这个变量必须是非解除参考(no dereference)的,即这个变量地址必须是有效的,

0, 表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。
1, 表示用户地址空间,这个不用多讲
2, 表示是设备地址映射空间

  1. 对 cond_syscall(sys_msgsnd)

cond_syscall(sys_msgsnd):如果存在sys_msgsnd(),则声明这个函数,在程序链接的时候使用这个函数;如果不存在sys_msgsnd()这个函数,就使用sys_ni_syscall()函数代替;
arch\xtensa\include\uapi>asm>unistd.h
unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX(可移植操作系统接口缩写) 操作系统 API 的访问功能的头文件的名称。该头文件由 POSIX.1 标准(可移植系统接口)提出,故所有遵循该标准的操作系统和编译器均应提供该头文件(如 Unix 的所有官方版本,包括 Mac OS X、Linux 等),其中包含了系统掉用号;
如:

#define __NR_msgsnd				167
__SYSCALL(167, sys_msgsnd, 4)
  1. 上述五个步骤讲述了用户态调用操作系统提供的API, 内核是对API如何支持的,例如 msgsnd
    但是在内核中, 函数原型的定义为 do_msgsnd,现在看两者是如何联系起来的
  2. 函数调用深度
    对于 ## 的使用: https://www.cnblogs.com/han-guang-xue/p/16856810.html#双-使用
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
		int, msgflg)
{
	long mtype;

	if (get_user(mtype, &msgp->mtype))
		return -EFAULT;
	return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...)				\
	SYSCALL_METADATA(sname, x, __VA_ARGS__)			\
	__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	SYSCALL_ALIAS(sys##name, SyS##name);				\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

最后对 __SYSCALL_DEFINEx 带入参数之后, 代码最终为

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys_msgsnd(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	static inline long SYSC_msgsnd(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS_msgsnd(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = SYSC_msgsnd(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	SYSCALL_ALIAS(sys_msgsnd, SyS_msgsnd);				\
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

最终执行了 SYSCALL_ALIAS(sys_msgsnd, SyS_msgsnd);

#define SYSCALL_ALIAS(alias, name) asm(			\
	".globl " VMLINUX_SYMBOL_STR(alias) "\n\t"	\
	".set   " VMLINUX_SYMBOL_STR(alias) ","		\
		  VMLINUX_SYMBOL_STR(name))
// 带入宏定义, 原型为
#define SYSCALL_ALIAS(alias, name) asm(".globl " #alias "\n\t"".set" alias ","#name)
#define SYSCALL_ALIAS(alias, name) asm(".globl sys_msgsnd\n\t".set" sys_msgsnd ","SyS_msgsnd)

这里是 内嵌汇编, 意思就是定义全局 sys_msgsnd参数, 并将 sys_msgsnd 设置值为 SyS_msgsnd; 也就是 sys_msgsnd 是声明的, SyS_msgsnd 是定义的, 要把定义的和声明的关联起来。

参考:汇编 .glole.set 介绍
https://howiezhao.github.io/2019/03/09/assembly/
https://blog.csdn.net/longintchar/article/details/80038843

  1. 通过如上分析了 do_msgsnd 和系统的 msgsnd 是如何关联起来的, 如何被调用, 其它的函数也是一样, 内核的代码设计很巧妙,借助内嵌汇编可以实现其它语言都无法实现的功能
posted @ 2022-12-09 13:39  韩若明瞳  阅读(145)  评论(0编辑  收藏  举报