当前位置:首页>Linux>观 Linux 源码,探 C 语言奥秘 —— const char *system 真的指向字符串吗?

观 Linux 源码,探 C 语言奥秘 —— const char *system 真的指向字符串吗?

  • 2026-09-05 04:03:28
观 Linux 源码,探 C 语言奥秘 —— const char *system 真的指向字符串吗?

环境说明:本文分析基于 x86_64 架构(64位系统),Linux 内核版本 v6.6.143。在此架构下,指针类型占用 8字节。

第一部分

// trace_event_class 结构体 定义
/* /include/linux/trace_events.h */
struct trace_event_class {
     const char  *system;
     void   *probe;
#ifdef CONFIG_PERF_EVENTS
     void   *perf_probe;
#endif
     int   (*reg)(struct trace_event_call *event,
                  enum trace_reg type, void *data);
     struct trace_event_fields *fields_array;
     struct list_head *(*get_fields)(struct trace_event_call *);
     struct list_head fields;
     int   (*raw_init)(struct trace_event_call *);
};

这是一个trace_event_class结构体的定义,其中

// system 字段
const char  *system;
字段、变量
  • 字段:从“结构体设计”的角度看
  • 变量:从“内存存储”的角度看

如上图所示,system是一个指针,它指向char类型的数据,即*system,并且const决定了*system变量只读(不可修改)。

疑惑:const决定了*system变量只读的本质是什么?是否意味着const决定了该内存中的数据只读呢?

第二部分

结构体像一张“蓝图”,而真正存在的是结构体实例。

// event_class_syscall_enter 结构体实例 定义
/* /kernel/trace/trace_syscalls.c */
struct trace_event_class __refdata event_class_syscall_enter = {
     .system  = "syscalls",
     .reg  = syscall_enter_register,
     .fields_array = syscall_enter_fields_array,
     .get_fields = syscall_get_enter_fields,
     .raw_init = init_syscall_trace,
};

这是一个trace_event_class结构体的实例event_class_syscall_enter的定义,其中

// system 字段赋值
.system  = "syscalls"

.system在语法层面上理解为 访问 system 字段,"syscalls"理解为字符串常量,但研究其本质,为下式

/* 假设 event_class_syscall_enter 的起始地址是 0xffff8000001000 */

*(const char **)(0xffff8000001000 + 0) = "syscalls";
                        ↑
        event_class_syscall_enter 的起始地址

从头开始分析这句话:

  • (const char **):二级指针
  • (0xffff8000001000 + 0):一个地址值(无符号整数)
  • *:解引用

我们发现,二级指针但是只解引用了一次,但赋的值是"syscalls",很奇怪。因此我们研究后总结出下式

/* 字符串"syscalls"的首地址,即字符's'的地址是0xffff8000000a5a00 */

*(const char **)(0xffff8000001000 + 0) = 0xffff8000000a5a00
                                                  ↑
"syscalls" 的首地址

原来真正写入的不是字符串常量,而是字符串常量所在的内存地址,并且这个地址值指向的内存区域是.rodata段,即只读数据段。此时,我们回到第一部分的那个疑惑:const决定了*system变量只读的本质是什么?是否意味着const决定了该内存中的数据只读呢?

我们就会发现,事实上const在此处提供的只读是语法层面的提醒,而非本质原因。真正起决定性作用的是“syscalls”作为字符串常量的身份——它保存在.rodata段,受操作系统物理机制保护,因此天然只读。

第三部分

我们进行总结一番:字符串常量在程序运行后,已经写入只读数据段了,且通过将字符串常量的地址赋值给system指针使其有意义。

同时,我们发现指针指向的其实是一个char类型数据,而真正有意义的却是字符串数据,这里面是否很矛盾?

实则不然,因为结构体实例的system指针提供一个首地址,真正要读取多长的数据,取决于的不是这个结构体实例本身,而是实现读取数据的函数。

/* /lib/vsprintf.c */
/* Handle string from a well known address. */
static char *string_nocheck(char *buf, char *end, const char *s,
       struct printf_spec spec)
{
   int len = 0;
   int lim = spec.precision;

while (lim--) {
      char c = *s++;
if (!c)
break;
if (buf < end)
           *buf = c;
      ++buf;
      ++len;
   }
return widen_string(buf, len, end, spec);
}
  • if (!c):这里判断c是否为'\0',若是,则停止

总结

本文通过研究 Linux v6.6.143 源码,对const char *system进行分析,实现了对 C语言 的指针与内存相关知识进行学习,并且得出了思考结论:指针指向的是一个char类型数据,而真正有意义的是字符串数据(通过其变量名可知其意义)。

最新文章

随机文章