Linux/Golang/glibC系统调用
Linux/Golang/glibC系统调用
本文主要通过分析Linux环境下Golang的系统调用,以此阐明整个流程
有时候涉略过多,反而遭到质疑~,写点文章证明自己实力也好
Golang系统调用
找个函数来分析
https://pkg.go.dev/os/exec#Cmd.Wait
源码文件在src/os目录下的: exec.go -> exec_unix.go -> pidfd_linux.go
https://github.com/golang/go/blob/2f6426834c150c37cdb1330b48e9903963d4329c/src/os/exec.go#L134



往下是系统调用: src/syscall目录的 syscall_linux.go -> ``


runtime层的:src/internal/runtime/syscall/syscall_linux.go,如下图,可以看见Sysacll6只有声明没有函数体,是个外部声明。

其函数体内容实际上位于同目录下的 .s 汇编文件,与编译时采用的架构工具链相关。
- 如编译工具链采用amd64则会编译链接位于 src/internal/runtime/syscall/asm_linux_amd64.s 的汇编文件

- 如编译工具链采用arm64,则会编译链接 src/internal/runtime/syscall/asm_linux_arm64.s 的汇编文件

by the way: 这里的语法是Golang汇编,属于Plan9分支。
golang汇编参考资料:
总结:Golang直接了当地使用汇编实现了系统调用(软中断号),而不需要再通过 libc 去调用系统调用库。这样的好处是不需要考虑 glibc 繁杂沉重的兼容性方案。
Linux 定义的系统调用表
- 很全的表格:https://gpages.juszkiewicz.com.pl/syscalls-table/syscalls.html
- ARM64架构:
- x86_32位架构tbl系统调用表: https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl
- x86_64位架构tbl系统调用表: https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl
本地审计工具:ausyscall --dump
Linux系统调用
内核实现
通过软中断陷入内核态/特权模式
和STM32 ARM核心一样,都是由一个异常向量表描述中断对应的Handler地址,软硬中断也是一样。
系统调用函数在 include/linux/syscalls.h中定义
我们拿asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode); 来分析
这里使用了汇编链接,它和上文提到的tbl系统调用表有关。我们拿x86/i386分析,arch/x86/entry/syscalls/syscall_32.tbl

中断号 295 架构i386即传统32位x86 sys_openat 是其回调函数/软中断Handler
linux/include/uapi/asm-generic/unistd.h
其实现位于 arch/处理器架构/include/之下
可以在 arch/x86 下搜索 openat
关于内核的系统调用这部分,本人会在再出一个文章。
Glibc 系统调用库
注意:Glibc属于库,不属于内核,是根文件系统的一部分。
我们在应用态陷入内核态,使用的c库里的open()等等函数,最后都是链式调用到了syscall()类的系统调用函数。
看glibc的源码,就会发现弯弯绕绕,最后是调用到
x86汇编 https://github.com/bminor/glibc/tree/master/sysdeps/unix/x86_64
arm汇编 https://github.com/bminor/glibc/tree/master/sysdeps/unix/arm
sysdeps/unix/arm/sysdep.S

sysdeps/unix/arm/sysdep.h

作用是将参数写入寄存器,让SoC自己触发软中断,根据Linux内核注册的软中断号执行对应地址段的函数,也就是我们常在STM32里注册定义的中断的handle函数。
Linux应用态到内核态例子
在线阅读代码:
- https://elixir.bootlin.com/glibc/glibc-2.29/source/include/errno.h#L37
- https://codebrowser.dev/glibc/glibc/io/read.c.html
- 带了编译产物的仓库 https://github.com/bminor/glibc/tree/a81cdde1cb9d514fc8f014ddf21771c96ff2c182
这些在线网站都不错,但为了高亮,所以我截图放了github的
我们在应用层调用 系统库的 fread()函数
其链接到glibc库的 libio/iofread.c

其中第44行可见其为 _IO_fread 声明了weak弱链接别名 fread,有关别名表可见编译产物如sysdeps/unix/syscalls.list等
做了一些预操作之后,调用libio/libio.h 声明的 libio/genops.c:_IO_sgetn

宏定义 libio/libioP.h

ps: JUMP2代表两个参数

展开宏

展开宏

展开宏

展开宏

结构体

也就说调用了 FP.__xsgetn(FP, DATA, N) ,展开差不多是
struct _IO_FILE_plus *THIS;
THIS->vtable->__xsgetn; 即_IO_xsgetn_t类型函数指针
即THIS/file对象的函数地址 size_t __xsgetn (FILE *FP, void *DATA, size_t N);
初始化位于
https://codebrowser.dev/glibc/glibc/libio/tst-vtables-common.c.html#jumps
相关的计数器

再看另一个,我们常用的fopen


compat_symbol (libc, _IO_old_file_fopen, _IO_file_fopen, GLIBC_2_0);

#define __open open
这里定向到了 open, 我们需要通过编译产物 sysdeps/unix/syscalls.list 找到其链接段

可在 io/open.c 找到函数 __libc_open 位于

由于弱定义,所以被以下覆盖 sysdeps/unix/sysv/linux/open.c

关键在于第43行的SYSCALL_CANCEL,其中的宏

展开宏INLINE_SYSCALL_CALL

展开宏

展开宏

展开宏

展开为
//展开
__INLINE_SYSCALL4(openat, AT_FDCWD, file, oflag, mode)
//继续展开为
__INLINE_SYSCALL(openat, 4, AT_FDCWD, file, oflag, mode)

//x86
#define SYS_ify(syscall_name) __NR_##syscall_name
#define INTERNAL_SYSCALL(name, nr, args...) \
internal_syscall##nr (SYS_ify (name), args)
//aarch64即 arm64
# define INTERNAL_SYSCALL_AARCH64(name, nr, args...) \
INTERNAL_SYSCALL_RAW(__ARM_NR_##name, nr, args)
x86的展开为 internal_syscall4 (__NR_openat, AT_FDCWD, file, oflag, mode)
可在 sysdeps/unix/sysv/linux/sh/arch-syscall.h 找到,其中断号为 295

对应openat的x86/i386中断号,刚好就是295,源码分析完全正确!每种平台的中断号都不一样,但是这样分析是正确的。
体验一下GNU宏地狱吧!
而ARM64的就高明得多,直接通过asm汇编指令写寄存器跳转执行__libc_do_syscall完成

glibc/sysdeps/unix/sysv/linux/arm/libc-do-syscall.S

总之是一个系统调用,等价于 openat(AT_FDCWD, file, oflag, mode);
总结:sysdeps是系统调用的实现,向上屏蔽细节,但是封装的过程用于一堆条件宏,根本没办法用代码分析工具,也难以调试。
对GNU LIBC代码的个人拙见:
好处:节省空间,较好的运行速度。
坏处:作为计算机世界的底层支持,这样还不如在编译器优化阶段下功夫,过多的黑魔法必然写出难以理解的代码,牵一发动全身,没有人愿意去改这堆疯狂嵌套的代码。作为新兴语言爱好者的我,始终认为程序要少点黑魔法,简洁直接才是最优解,剩下的东西都应该交给编译器,何况系统调用的耗时从来就不在这里,主要性能影响都是在内核态用户态切换的时候,并不在c库本身。
GNU的代码向来很难读,glibc更是个寄吧,各种宏和硬链接乱飞,有再好的代码阅读工具也难找出来。
但要说来,说到底还是c语言/链接器的设计缺陷,没办法更好的实现动态表和静态表。(多态组合、编译期间的函数静态段的多分支链接)
微软的代码是框架难以理解,因为他们也不给出架构图和代码结构的...,,而GNU的代码是宏和链接难以理解。
最后
请指正批评!感谢阅读。
Linux/Golang/glibC系统调用的更多相关文章
- 基于int的Linux的经典系统调用实现
先说明两个概念:中断和系统调用 一 系统调用: 是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何和内核打交道的. 1, Linux系统调用:2.6.19版内 ...
- linux使用glibc版本安装mysql8.0.12
1.前言 使用yum安装虽然很方便,但是如果要是在没有公网的环境下,是没有办法使用yum源的.所以我们可以使用mysql提供的glibc版本的安装包,进行安装. 但是在安装之前,一定要将以前的版本删除 ...
- 用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程
GDB的那些奇淫技巧 evilpan 收录于 Security 2020-09-13 约 5433 字 预计阅读 11 分钟 709 次阅读 gdb也用了好几年了,虽然称不上骨灰级玩家,但 ...
- Linux 中open系统调用实现原理【转】
转自:http://blog.chinaunix.net/uid-25968088-id-3426026.html 目录 OPEN系统调用过程 Open在内核里面的入口函数时sys_open Sys_ ...
- (转)linux下的系统调用函数到内核函数的追踪
转载网址:http://blog.csdn.net/maochengtao/article/details/23598433 使用的 glibc : glibc-2.17使用的 linux kerne ...
- Linux下的系统调用
张雨梅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10000 1.linux的的用户态与内核 ...
- linux内核增加系统调用--Beginner‘s guide
Linux内核中设置了一组用于实现系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似明知是系统调用由操作系统核心提供,运行于核心态,而普通的函数调用由函数库或用户自己提供,运行于用户态. ...
- linux OSlab4 添加自定义系统调用
http://blog.csdn.net/ly01kongjian/article/details/8947285 http://www.cnblogs.com/hoys/archive/2011/0 ...
- Linux之select系统调用_1
SYNOPSIS /* According to POSIX.1-2001 */ #include <sys/select.h> /* According to earlier stand ...
- 《Linux内核分析》 week6作业-Linux内核fork()系统调用的创建过程
一.进程控制块PCB-stack_struct 进程在操作系统中都有一个结构,用于表示这个进程.这就是进程控制块(PCB),在Linux中具体实现是task_struct数据结构,它主要记录了以下信息 ...
随机推荐
- OpenHarmony持久化存储UI状态:PersistentStorage
前两个小节介绍的LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStor ...
- Spring的事务管理方式编程式和声名式
spring的事务管理方式: 一.声名式 二.编程式 事务:查询不会影响数据的改变,对于增删改必须进行事务的管理.如果没有事务管理spring也提供了默认传播方式REQUIRED 一.声名式事务管理( ...
- 踩坑指南:入门OpenTenBase之监控篇
本次监控将采用Prometheus.Grafana可视化工具以及postgres_exporter对OpenTenBase进行全面监控和优化. 安装监控 Docker安装 1.Docker要求 Cen ...
- HarmonyOS课程体验官招募(第四期),寻找乐于分享,精益求精的伙伴
华为开发者联盟HarmonyOS课程体验官(第四期)活动,开始招募啦! 如果你精益求精.乐于分享:如果你愿意为学堂课程优化改进出谋划策,那就快来加入我们吧!学堂期待与你共同成长.一起进步! [活动 ...
- TiDB Vector 抢先体验之用 TiDB 实现以图搜图
本文首发自 TiDB 社区专栏:https://tidb.net/blog/0c5672b9 前言 最早知道 TiDB 要支持向量化的消息应该是在23年10月份左右,到第一次见到 TiDB Vecto ...
- DataWorks搬站方案:Airflow作业迁移至DataWorks
简介: DataWorks提供任务搬站功能,支持将开源调度引擎Oozie.Azkaban.Airflow的任务快速迁移至DataWorks.本文主要介绍如何将开源Airflow工作流调度引擎中的作业迁 ...
- 数仓架构的持续演进与发展 — 云原生、湖仓一体、离线实时一体、SaaS模式
简介: 数据仓库概念从1990年提出,经过了四个主要阶段.从最初的数据库演进到数据仓库,到MPP架构,到大数据时代的数据仓库,再到今天的云原生的数据仓库.在不断的演进过程中,数据仓库面临着不同的挑战. ...
- Flink 1.13,面向流批一体的运行时与 DataStream API 优化
简介: 在 1.13 中,针对流批一体的目标,Flink 优化了大规模作业调度以及批执行模式下网络 Shuffle 的性能,以及在 DataStream API 方面完善有限流作业的退出语义. 本文由 ...
- MySQL 8.0 Server层最新架构详解
简介: 本文基于MySQL 8.0.25源码进行分析和总结.这里MySQL Server层指的是MySQL的优化器.执行器部分.我们对MySQL的理解还建立在5.6和5.7版本的理解之上,更多的是对比 ...
- [FAQ] iCloud 照片共享, 收到xx集团邀你xx, 拒绝 or 关闭 ?
如果你收到邀请日历这是垃圾邮件和简单地选择"拒绝"选项,这个问题不会消失. 事实上,很可能增加,因为垃圾邮件发送者知道该帐户被激活.这同样适用于iCloud的照片共享. 对于iCl ...