- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
Linux/Golang/glibC系统调用 。
本文主要通过分析Linux环境下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 汇编文件,与编译时采用的架构工具链相关.
by the way: 这里的语法是Golang汇编,属于Plan9分支。 golang汇编参考资料
总结:Golang直接了当地使用汇编实现了系统调用(软中断号),而不需要再通过 libc 去调用系统调用库。这样的好处是不需要考虑 glibc 繁杂沉重的兼容性方案.
本地审计工具:ausyscall --dump 。
通过软中断陷入内核态/特权模式 和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属于库,不属于内核,是根文件系统的一部分。 我们在应用态陷入内核态,使用的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函数.
在线阅读代码:
我们在应用层调用 系统库的 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),
初始化 _IO_jump_t 位于 JUMP_INIT 。
宏展开 函数定义 。
相关的计数器 。
再看另一个,我们常用的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系统调用的文章就讲到这里了,如果你想了解更多关于Linux/Golang/glibC系统调用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有这个代码: System.err.print("number of terms = "); System.out.println(allTerms.size()); System.err
我有以下问题:在操作系统是 Linux 的情况下和在操作系统是 MacOs 的情况下,我必须执行不同的操作。 所以我创建了以下 Ant 脚本目标: /u
我正在调用 system("bash ../tools/bashScript\"This is an argument!\"&"),然后我正在调用 close(socketFD) 直接在 system
使用最初生成的随机元素来约束随机数组的连续元素是否有效。 例如:我想生成一组 10 个 addr、size 对来模拟典型的内存分配例程并具有如下类: class abc; rand bit[5:0
我正在创建一个必须使用system(const char*)函数来完成一些“繁重工作”的应用程序,并且我需要能够为用户提供粗略的进度百分比。例如,如果操作系统正在为您移动文件,它会为您提供一个进度条,
我即将编写一些项目经理、开发人员和业务分析师会使用的标准/指南和模板。目标是更好地理解正在开发或已经开发的解决方案。 其中一部分是提供有关记录解决方案的标准/指南。例如。记录解决/满足业务案例/用户需
在开发使用压缩磁盘索引或磁盘文件的应用程序时,其中部分索引或文件被重复访问(为了论证,让我们说一些类似于 Zipfian 分布的东西),我想知道什么时候足够/更好地依赖操作系统级缓存(例如,Debia
我们编写了一个 powershell 脚本,用于处理来自内部系统的图像并将其发送到另一个系统。现在,业务的另一部分希望加入其中,对数据进行自己的处理,并将其推送到另一个系统。打听了一下,公司周围有几个
我正在尝试朗姆酒我的应用程序,但我收到以下错误:System.Web.HttpUnhandledException:引发了“System.Web.HttpUnhandledException”类型的异
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
所以我在其他程序中没有收到此错误,但我在这个程序中收到了它。 这个程序是一个我没有收到错误的示例。 #include int main() { system("pause"); } // en
我在 c# System.URI.FormatExption 中遇到问题 为了清楚起见,我使用的是 Segseuil 的 Matlab 方法,并且它返回一个图片路径 result。我想为其他用户保存此
我正在尝试像这样设置文本框的背景色: txtCompanyName.BackColor = Drawing.Color.WhiteSmoke; 它不喜欢它,因为它要我在前面添加系统,例如: txtCo
请帮助我解决 System.StackOverflowException我想用 .aspx 将记录写入数据库我使用 4 层架构来实现这一切都正常但是当我编译页面然后它显示要插入数据的字段时,当我将数据
我使用了一些通常由系统调用的API。 因此,我将 android:sharedUserId="android.uid.system" 添加到 manifest.xml, 并使用来自 GIT 的 And
我正在尝试创建一个小型应用程序,它需要对/system 文件夹进行读/写访问(它正在尝试删除一个文件,并创建一个新文件来代替它)。我可以使用 adb 毫无问题地重新挂载该文件夹,如果我这样做,我的应用
我想从没有 su 的系统 priv-app 将/system 重新挂载为 RW。如何以编程方式执行此操作?只会用 Runtime.getruntime().exec() 执行一个 shell 命令吗
我正在尝试制作一个带有登录系统的程序我对此很陌生,但我已经连续工作 8 个小时试图解决这个问题。这是我得到的错误代码 + ServerVersion 'con.ServerVersion' threw
当我“构建并运行”Code::Blocks 中的程序时,它运行得非常好!但是当我从“/bin”文件夹手动运行它时,当它试图用 system() 调用“temp.bat”时,它会重置。这是为什么?它没有
我想使用 system/pipe 命令来执行具有特殊字符的命令。下面是示例代码。通过系统/管道执行命令后,它通过改变特殊字符来改变命令。我很惊讶地看到系统命令正在更改作为命令传递的文本。 run(ch
我是一名优秀的程序员,十分优秀!