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远程到目录 查看内核代码
如图:
linux 内核函数接口如何提供给用户态
- 内核态的函数接口不是直接提供给用户态调用的, 比如用户态调用
msgsnd
函数 - 该函数是通过
msg.h
提供的
extern int msgsnd (int __msqid, const void *__msgp, size_t __msgsz,
int __msgflg);
- 在内核代码中,
sys_msgsnd
, 对系统API函数相对内核的API可以查看内核文件./tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
- 在内核的代码接口声明为
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, 表示是设备地址映射空间
- 对 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)
- 上述五个步骤讲述了用户态调用操作系统提供的API, 内核是对API如何支持的,例如
msgsnd
但是在内核中, 函数原型的定义为do_msgsnd
,现在看两者是如何联系起来的 - 函数调用深度
对于##
的使用: 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
- 通过如上分析了
do_msgsnd
和系统的msgsnd
是如何关联起来的, 如何被调用, 其它的函数也是一样, 内核的代码设计很巧妙,借助内嵌汇编可以实现其它语言都无法实现的功能
本文来自博客园踩坑狭,作者:韩若明瞳,转载请注明原文链接:https://www.cnblogs.com/han-guang-xue/p/16968708.html