
Linux开发
choumin
这个作者很懒,什么都没留下…
展开
-
在 Linux C 中获得所有环境变量
在 Linux C 中获得所有环境变量 ...原创 2024-04-07 14:39:17 · 498 阅读 · 0 评论 -
错误:expected ‘{’ at end of input
QEMU 编译问题解决原创 2023-06-09 16:54:01 · 703 阅读 · 0 评论 -
内核源码中的 arch/xxx/include/asm/Kbuild 文件
内核源码中的 arch/xxx/include/asm/Kbuild 文件学习原创 2023-03-22 16:57:29 · 240 阅读 · 0 评论 -
使用aim7测试内核性能变化
使用 aim7 测试内核性能变化情况原创 2023-03-06 20:57:21 · 558 阅读 · 0 评论 -
Linux 4.19 内核中 spinlock 概览
Linux 4.19 内核 spinlock 相关文件梳理原创 2023-02-24 22:15:10 · 955 阅读 · 0 评论 -
编译内核时更新 xxx_defconfig 文件的方法
如何根据旧的 xxx_defconfig 生成新的 xxx_defconfig原创 2023-02-14 10:40:10 · 414 阅读 · 0 评论 -
使用 patchelf 修改 ELF 文件的动态链接器路径
使用 patchelf 修改 ELF 文件的动态链接器路径示例。原创 2023-01-27 14:23:45 · 2360 阅读 · 0 评论 -
ioctl cmd 不能等于 2 的小问题
平时很少写内核模块,今天遇到一个小问题,记录如下原创 2022-11-24 22:26:55 · 402 阅读 · 0 评论 -
gcc的always_inline属性
always_inline 属性使得 gcc 编译器:1)忽略 -fno-inline2)忽略对 inline 的限制,例如,通常情况下带有 alloca 调用的函数是不能 inline 的,但 alway_inline 可以下面是内核中使用的一个 alway_inline 例子,出现在 ./arch/x86/include/asm/cpufeature.h 文件中。应该算是很有代表性了,因为这里的需求是对函数内部的某些指令进行在线改写,且改写的条件受函数入参影响,如果不强制使用 inline,那么将原创 2021-03-24 11:57:46 · 2035 阅读 · 0 评论 -
内核中的alternative宏
通过 alternative() 宏,内核可以在运行时,通过判断当前 CPU 是否支持某些 feature, 实现对内核代码的在线优化(不关机、不换内核的情况下在线改写某些内核指令),达到加速内核执行的目的,下面以 x86 为例描述其大概的实现过程。1)alternative() 宏定义在 arch/x86/include/asm/alternative-asm.h 定义了 ALTERNATIVE() 宏,如下所示:.macro ALTERNATIVE oldinstr, newinstr, f原创 2021-03-23 08:38:56 · 1544 阅读 · 0 评论 -
内核中的quicklist
内核在分配和释放页帧时需要做很多事情,会耗费较多的时钟周期,为此,我们可以通过一个链表来缓存将要被释放的页,也就是不真正的去释放,而是将其放在一个链表上,下次在分配页时直接从链表上取,这样可以大大减少页帧在释放和分配时的工作量,进而提高性能。在内核中,有这样一个链表——quicklist,它是 per cpu 变量,每个 cpu 对应一个 struct quicklist 类型的结构体变量,通过这个结构体实现了缓存页帧的管理。struct quicklist 的定义如下:struct quicklis原创 2021-03-08 19:23:18 · 255 阅读 · 0 评论 -
使用glibc xdr对结构体进行编解码
glibc 中的 rpc/xdr.h 提供了很多 xdr_xxx()接口,可以用一种跨平台的方式对任意数据进行编码和表示。下面介绍如何对结构体数据进行跨平台的编码和解码,主要涉及以下几个步骤:1)使用 rpc 语言定义结构体(类似于 thrift 中的 IDL 文件),使用 rpcgen 生成对应的 .h 和 .c 文件2)使用 xdrstdio_create() 初始化 XDR 对象,并调用上述文件自动生成的接口对结构体数据进行编码,得到编码后的内存 buffer3)使用 xdrmem_cr.原创 2021-03-06 23:25:41 · 1454 阅读 · 0 评论 -
fmemopen和open_memstream
glibc 中的 fmemopen() 和 open_memstream() 函数可以实现在一段内存上进行 IO 操作,它们的返回值都是 FILE * 类型,利用这个文件指针,可以调用 fwrite()、fread() 等流操作函数,操作结果可以反映到这段内存中。fmemopen() 使用示例:#include <stdio.h>#include <stdlib.h>#include <string.h>char *write_to_buf(char *原创 2021-03-06 22:11:40 · 2518 阅读 · 0 评论 -
使用unshare创建新的pid namespace
在 linux 上,可使用 unshare 命令或 unshare()系统调用来创建一个新的 namespace,其中也包括 pid namespace(内核版本至少需要是3.8),需要注意的一个小点是,默认情况下,unshare 创建的 pid namespace 是作用在被调程序的子进程上,如下所示:$ sudo unshare -p ./getpidpid: 27666使用 --fork 表示创建一个子进程,并在子进程中运行命令。可以看到 pid namespace 在子进程上生效了,第原创 2021-02-25 10:36:48 · 1142 阅读 · 0 评论 -
动态链接的可执行程序的加载过程
1)内核在装载完 ELF 可执行文件以后就返回到用户空间,将控制权交给程序的入口。对于不同链接形式的 ELF 可执行文件,这个程序的入口是有区别的。对于静态链接的可执行文件来说,程序的入口就是 ELF 文件头里面的 e_entry 指定的入口;对于动态链接的可执行文件来说,如果这时候把控制权交给 e_entry 指定的入口地址,那么肯定是不行的,因为可执行文件所依赖的共享库还没有被装载,也没有进行动态链接。所以对于动态链接的可执行文件,内核会分析它的动态链接器地址(在“.interp”段),将动态链接器映射原创 2021-02-22 14:49:27 · 489 阅读 · 1 评论 -
在linux中使用setns()设置pid namespace
以下代码展示了 setns() 的用法,#define _GNU_SOURCE#include <fcntl.h>#include <sched.h>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h>#in原创 2021-02-10 08:47:36 · 3573 阅读 · 0 评论 -
glibc中系统调用的汇编封装
在 glibc 中有三种方式封装系统调用,包括汇编封装、通过宏进行封装、定制封装。对于汇编封装的系统调用,需要在 syscalls.list 文件中进行配置,例如:sysdeps/unix/sysv/linux/x86_64/syscalls.list 文件中配置了几个与 socket 相关的系统调用,其内容如下:# File name Caller Syscall name # args Strong name Weak namesarch_prctl EXTRA arch_prctl i:i原创 2021-02-04 20:28:56 · 627 阅读 · 0 评论 -
在linux下产生core dump文件
1)、通过 kill() 系统调用给进程发送以下信号时,其默认行为是终止进程和产生 core 文件:SIGBUSSIGFPESIGILLSIGIOTSIGQUITSIGSEGVSIGSYSSIGTRAPSIGUNUSEDSIGXCPUSIGXFSZ2)、abort() 会产生core文件3)、raise() 会产生core文件...原创 2021-01-27 13:36:07 · 634 阅读 · 0 评论 -
多线程中的uid
在内核级别,用户 ID 和 组 ID 是每个线程的属性,然而,POSIX 标准要求同一个进程里的多个线程共享相同的认证信息(credentials)。在 NPTL (Native POSIX Threads Library)线程实现中,通过对某些系统调用进行封装,从而支持了这一要求。这里的封装函数包括 setuid(),它通过信号技术确保了当其中某个线程改变认证信息后,其他所有线程也会跟着改变对应的认证信息。在glibc中,setuid() 最终会调用 __nptl_setxid(),其定义在 npt原创 2021-01-25 20:09:20 · 560 阅读 · 1 评论 -
多线程中的pid
由一个进程创建的多个线程具有相同的 pid,也就是在这些线程中分别调用 getpid(),得到的结果是一样的。这里的 pid 其实是 task struct(进程描述符结构体)中的 tgid,它是线程的 group ID,也是主线程(它创建了子线程)的 PID。而 task struct 中的 pid 其实是线程的 ID, 也可以叫作 tid(thread ID),对于多线程而言,这些 tid 是不同的。借用 stackoverflow 上的一个回答来解释这里面的逻辑,下面这张图形象的描述了多进程和多原创 2021-01-25 18:18:21 · 1870 阅读 · 0 评论 -
glibc中vdso系统调用的定义
为了新增一个 vdso 系统调用,需要同时修改内核和glibc。glib支持多种体系结构和操作系统,在gblic中新增一个vdso系统调用,需要在对应体系结构目录下新增一个包含该系统调用的.c文件,比如 ./sysdeps/unix/sysv/linux/x86/gettimeofday.c 文件,它定义了 gettimeofday() 这个系统调用的 vdso 版本的调用流程,内容如下所示:#include <sys/time.h>#ifdef SHARED# include.原创 2021-01-20 15:20:47 · 572 阅读 · 0 评论 -
vdso原理学习
首先,借用一张图来说明 vdso 的原理,下面是描述 vdso 在 arm64 上的工作原理:总的来说,vdso 分为用户态部分和内核态部分。vdso 用户态部分提供 __vdso_xxx() 接口(或者是 __kernel_xxx() 接口,如上图所示),这些接口由glibc 调用并将结果返回给用户程序,它们的实现也比较简单,就是读取 vdso_data 里面的内容并进行简单处理,然后将结果返回给 glibc。vdso_data 是一个结构体,这个数据结构也...原创 2021-01-20 13:59:56 · 4275 阅读 · 0 评论 -
x86中vdso数据段的初始化及更新和使用
1、vdso 数据段的初始化1)数据段的定义:vdso 数据段由内核进行声明和定义,其中,在链接脚本 arch/x86/entry/vdso/vdso-layout.lds.S 里指定了 vdso 的数据段的名称和位置,相关内容如下:SECTIONS { vvar_start = . - 3 * PAGE_SIZE; vvar_page = vvar_start; #define EMIT_VVAR(name, offset) vvar_ ## name = vvar_p原创 2021-01-08 18:19:10 · 1057 阅读 · 0 评论 -
使用daemon()让C程序运行在后台
在linux中,可以使用daemon()函数让程序运行在后台,这个函数的原型是:#include <unistd.h>int daemon(int nochdir, int noclose);其中,如果 nochdir 为 0,则 daemon() 将当前工作目录改变为 "/",否则当前工作目录保持不变;如果 nocolose 为 0,则 daemon() 将标准输入、标准输出、标准错误输出 重定向为/dev/null,否则保持不变。示例程序:#include &l.原创 2020-12-20 20:52:40 · 1155 阅读 · 0 评论 -
vdso学习
1、只有一小部分系统调用是通过vdso方式实现的,主要涉及get型的系统调用。例如:类似gettimeofday之类的调用,如果每次都陷入内核去拿一个时间戳,会消耗比较多的上下文切换时间,因此,内核把时间戳放在一个公共的可以暴露给任何用户进程的地方,将其映射进用户进程空间。一般来说,通过vdso方式获得的都是一些不太敏感的信息,此时可以不用考虑特权等级,直接在用户态来获取。同时,vdso功能需要libc支持。2、可以使用这篇博客上介绍的方法将进程地址空间中vdso段的内容dump到文件,可以发现,不同体系原创 2020-12-18 09:17:47 · 615 阅读 · 0 评论 -
在linux C中使用unlink删除文件
在linux C中,unlink() 可以删除一个文件,该函数的定义在 unistd.h 头文件里,下面看一下使用方法:// main.c#include <stdio.h>#include <unistd.h>#include <stdlib.h>int main(void) { int fd; char *fname= "a.txt"; if ((fd = creat(fname, 0600)) == -1) { pri原创 2020-12-16 13:21:26 · 1186 阅读 · 0 评论 -
edk2中的全局变量gST和gBS
edk2开发环境用来开发UEFI驱动和UEFI应用,以及BIOS固件。在阅读edk2源码的过程中,经常遇到找不到函数指针所指向的函数。这既是面向对象带来的好处,也是坏处。edk2中有两个重要的全局变量,他们分别是:gST、gBS。现在就来梳理一下这两个常见全局变量的定义、初始化。1、gST、gBS的出处在MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c中被定义和初始化。 2、efi文件的加载和执行一...原创 2020-11-21 09:57:52 · 1739 阅读 · 0 评论 -
Gcc静态链接过程学习
1、在linux中,可执行文件的格式是ELF,Gcc编译出来的目标文件也是ELF格式。目标文件中除了有编译后的机器指令代码、数据等,还包括链接时所需要的一些重要信息,比如重定位表、符号表、调试信息、字符串表等。目标文件将这些信息按不同的属性,以“段”的形式存储。例如,程序源代码编译后的机器指令经常被放在代码段(.text)里,全局变量和局部静态变量数据经常放在数据段(.data)。除了.text段 和 .data段,ELF中可能还存在其他的段,比如,.bss段、.comment段、.debug段、.roda原创 2020-10-08 00:07:10 · 527 阅读 · 0 评论 -
python初始化二维数组的小问题
python二维数组初始化问题引子:今天在写用Python写01背包的算法课作业时,发现一个关于二维数组的小问题,本来期末已经很忙了,但这个问题还是值得记录下来的,因为这不是第一次遇到了,这次花了将近一个小时才搞明白。问题:以如下方式初始化一个二维数组,在使用的过程中有没有问题?matrix = [[0] * n] * m有问题!可以做个实验:>>> m原创 2017-06-10 16:49:26 · 4416 阅读 · 0 评论 -
数组名取地址带来的问题
我们都知道数组名取地址的值和数组的首地址是同一个值,但是他们的指类是不同的,如果强转的话是会出问题的。看看下面这个程序片段:#include int foo(char **t){ printf("t: %p\n", t); printf("*t: %p\n", *t); printf("**t: %p\n", **t); return 0;}int main(vo原创 2016-05-08 18:56:31 · 528 阅读 · 0 评论 -
内存对齐的malloc、realloc、free
最近需要用到内存对齐的内存分配函数,参考网上的一些实现,自己也试着写了一个,经过测试可以使用。欢迎拍砖,微笑。 0.内存对齐的大小#define CACHE_LINE_SIZE 64 /*内存对齐的大小,我们选用一个cache line的大小*/#define CACHE_LINE_MASK (CACHE_LINE_SIZE - 1) /*用于计算对齐的掩码*/1.内存对齐的malloc(原创 2016-04-27 10:45:09 · 1384 阅读 · 0 评论 -
linux/C 进度条实现
最近在生成一个比较大的文件时,需要显示进度条,于是自己也想实现一下。 我觉得主要注意到两个地方就可以了:1).知道字符‘\b’可以退格,了解视觉暂留;2).记录每次显示进度条时所用到的字符个数,以便下次进行退格。1.显示进度条函数/*progress为进度百分比,取值为0~100, last_char_count为上一次显示进度条时所用到的字符个数*/int display_progress(i原创 2016-04-27 11:16:24 · 3428 阅读 · 1 评论