当前位置:首页>Linux>Linux 内核:深入理解proc虚拟文件系统

Linux 内核:深入理解proc虚拟文件系统

  • 2026-01-29 18:07:52
Linux 内核:深入理解proc虚拟文件系统

Linux 的 /proc 文件系统(procfs)是一种虚拟(伪)文件系统,其核心作用是为用户态程序提供访问内核数据的便捷接口。与传统磁盘文件系统不同,/proc目录下的所有文件和子目录均不占用实际磁盘存储空间,而是由内核在系统运行过程中实时动态生成。该文件系统的主要用途包括获取系统及进程的各类运行信息、开展系统调试工作,以及进行日常的系统管理操作。

procfs整合了丰富的动态信息,涵盖硬件配置、内核运行参数、当前活跃进程状态等多个维度,允许用户和应用程序通过标准的文件操作方式,轻松读取并交互系统当前的运行状态。

一、 认识 proc

1.1 proc:不是 “文件” 的文件系统

很多同学第一次接触proc,都会有个疑问:为什么ls /proc能看到一堆文件,可du -sh /proc查看大小却几乎为0?

其实答案很简单——proc根本不是传统的物理文件系统,而是内核“虚拟”出来的,咱们先通过一段简单的命令实操,直观感受下它的特别之处:

# 查看proc目录大小(几乎为0,不占用物理磁盘)du -sh /proc# 查看proc下的核心文件(动态生成,每次读取内容可能不同)cat /proc/meminfo | head -10# 对比物理文件系统(ext4有明确大小,内容固定)du -sh /etc/hosts

实操后就能发现,proc挂载于/proc目录,所有文件内容都由内核在内存中动态生成,不占用任何物理磁盘空间。

它的核心特性,用3句话总结,结合上面的命令更好理解:

1.  实时性:每次读取/proc/meminfo,内核都会重新获取当前内存状态,所以两次读取的内容可能不一样;

2.  双向性:既能读(比如看内存),也能写(比如修改内核参数);

3.  零存储:文件大小显示为0,因为根本不存到磁盘,只存在于内存中。这和ext4等物理文件系统,有着本质区别。

proc 文件系统通常挂载于/proc目录下,这是它与用户空间交互的主要入口。你可以把它想象成一个实时更新的信息库,内核通过这个库向用户和应用程序展示系统的各种状态信息。例如,当你查看/proc目录下的文件时,你会发现有些文件的大小显示为 0 字节,但当你读取它们的内容时,却能获取到丰富的系统信息,这正是 proc 文件系统的神奇之处。

proc 文件系统具有几个核心特性,使其在 Linux 系统中发挥着不可或缺的作用。首先是它的 “实时性”,由于文件内容是由内核动态生成的,所以 proc 文件系统能够实时反映系统的运行状态。无论是系统的 CPU 使用率突然飙升,还是内存占用情况发生变化,你都可以通过读取 proc 文件系统中的相关文件,第一时间获取到这些信息。

其次是 “双向性”,proc 文件系统不仅支持用户读取系统信息,还允许用户通过修改某些文件来调整内核参数,实现对系统行为的动态控制。比如,你可以通过修改/proc/sys/net/ipv4/ip_forward文件的值,来开启或关闭 IP 转发功能,而无需重启系统。

最后是 “零存储”,正如前面提到的,proc 文件系统中的文件并不占用物理磁盘空间,它们只存在于内存中,这使得 proc 文件系统在提供丰富信息的同时,不会对系统的存储资源造成任何负担。

与传统的物理文件系统,如 ext4 相比,proc 文件系统的差异一目了然。ext4 文件系统用于管理磁盘上的文件和目录,文件内容存储在磁盘的物理块中,具有持久化存储的特点。而 proc 文件系统则专注于提供系统运行时的动态信息,文件内容实时更新,不具备持久化存储的功能。

1.2  proc 目录结构

1.2.1 进程专属空间:以 PID 命名的目录

聊完proc的整体特性,咱们聚焦它的目录结构——最核心的就是两大板块,先看第一个:以PID命名的进程专属目录。咱们平时排查进程问题,靠的就是这个板块,先上实操命令,跟着做一遍就懂了:

# 查看当前系统所有运行的进程PID(对应/proc下的数字目录)ps -ef | head -5# 随便选一个PID(比如1,init进程),查看它的核心信息cat /proc/1/cmdline  # 进程启动命令cat /proc/1/status   # 进程状态(CPU、内存占用)ls -l /proc/1/fd     # 进程打开的文件描述符

实操后就能明白:/proc下每个数字命名的目录,都对应一个运行中的进程(PID就是目录名),里面的文件的都是这个进程的“身份档案”,重点咱们讲4个最常用的,结合代码/命令拆解:

首先是/proc/[pid]/cmdline,这个文件记录了进程启动时的命令行参数。通过查看这个文件,可以清楚地了解到该进程是如何被启动的,这对于排查进程启动问题非常有帮助。例如,当我们怀疑某个进程启动时参数配置错误时,就可以查看cmdline文件来验证。

/proc/[pid]/status文件则提供了可读性较高的进程状态信息,其中包含了进程的 CPU 占用情况、内存占用情况、进程状态(如运行、睡眠、停止等)等关键信息。它就像是进程的一份健康报告,让我们一眼就能了解到进程的基本运行状况。比如,当发现系统性能下降时,可以通过查看各个进程的status文件,找出占用资源较多的进程,进而进行优化。

/proc/[pid]/fd目录下存放着进程打开的文件描述符的符号链接。每个符号链接都对应着进程当前打开的一个文件、套接字或设备等资源。通过查看这个目录,可以了解到进程正在访问哪些资源,这对于分析进程的行为和排查资源占用问题非常重要。例如,当我们怀疑某个进程占用了过多的文件资源时,可以通过查看fd目录下的符号链接来确认。

/proc/[pid]/maps文件记录了进程的内存映射信息,包括进程的代码段、数据段、堆、栈以及共享库等在内存中的映射关系和权限信息。这对于理解进程的内存使用情况和调试内存相关的问题非常有帮助。比如,当遇到进程内存泄漏的问题时,可以通过分析maps文件来查找可能存在问题的内存区域。

为了更直观地理解,我们以查看 init 进程(PID 通常为 1)的状态为例。在终端中输入cat /proc/1/status,你会看到类似以下的输出:

Name:   systemdState:  S (sleeping)Tgid:   1Ngid:   0Pid:    1PPid:   0TracerPid:      0Uid:    0       0       0       0Gid:    0       0       0       0FDSize64Groups0 VmPeak:     170908 kBVmSize:     170908 kBVmLck:         0 kBVmPin:         0 kBVmHWM:       5940 kBVmRSS:       5940 kBRssAnon:     2412 kBRssFile:     3528 kBRssShmem:       0 kBVmData:     10924 kBVmStk:       1764 kBVmExe:        136 kBVmLib:      10848 kBVmPTE:        152 kBVmSwap:        0 kBThreads:    1SigQ:   0/1416SigPnd0000000000000000ShdPnd0000000000000000SigBlk0000000000000000SigIgn0000000000001000SigCgt0000000000000800CapInh0000000000000000CapPrm0000001fffffffffCapEff0000001fffffffffCapBnd0000001fffffffffCapAmb0000000000000000Seccomp:        0Cpus_allowed:   fCpus_allowed_list:      0-3Mems_allowed:   00000000,00000001Mems_allowed_list:      0voluntary_ctxt_switches:        120nonvoluntary_ctxt_switches:     43

从这个输出中,我们可以了解到 init 进程的名称为systemd,当前状态为睡眠(S),进程 ID 为 1,父进程 ID 为 0,以及它的内存使用情况、线程数等详细信息。

1.2.2 系统全局信息库:通用文件与 /proc/sys 目录

除了进程专属目录,/proc下还有一大板块——通用文件和/proc/sys目录,这是咱们查看系统全局信息、调优内核的核心入口。还是老规矩,先实操,再讲原理,新手也能跟上:

# 1. 查看CPU硬件信息(比lscpu更详细,源码级信息)cat /proc/cpuinfo | grep -E "model name|cpu cores"# 2. 查看内存详情(free命令的底层数据源)cat /proc/meminfo | grep -E "MemTotal|MemFree|Cached"# 3. 查看系统负载(判断服务器是否繁忙)cat /proc/loadavg# 4. 动态修改内核参数(临时开启IP转发)echo 1 > /proc/sys/net/ipv4/ip_forward# 验证是否生效cat /proc/sys/net/ipv4/ip_forward

上面这几条命令,是运维和开发中最常用的,咱们逐个拆解,结合底层逻辑,不搞纯理论:

/proc/cpuinfo文件包含了 CPU 的硬件信息,如 CPU 型号、核心数、主频、缓存大小、支持的指令集等。通过查看这个文件,可以了解到系统所使用的 CPU 的详细规格,这对于评估系统性能和进行性能优化非常有帮助。例如,当需要在系统上运行一些对 CPU 性能要求较高的任务时,可以先查看cpuinfo文件,了解 CPU 的性能参数,以便判断系统是否能够满足任务的需求。

processor       : 0vendor_id       : GenuineIntelcpu family      : 6model           : 158model name      : Intel(R) Core(TM) i7-8700K CPU @ 3.70GHzstepping        : 10microcode       : 0xf0cpu MHz         : 3700.000cache size      : 12288 KBphysical id     : 0siblings        : 12core id         : 0cpu cores       : 6apicid          : 0initial apicid  : 0fpu             : yesfpu_exception   : yescpuid level     : 22wp              : yesflags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_eppbugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihitbogomips        : 7400.00clflush size    : 64cache_alignment : 64address sizes   : 46 bits physical, 48 bits virtualpower management:

从上述输出中,可以看到这是一颗 Intel Core i7-8700K CPU,具有 6 核心 12 线程,主频为 3.70GHz,缓存大小为 12288KB 等信息。

/proc/meminfo文件提供了系统内存使用的详细信息,包括总内存、空闲内存、已用内存、缓存内存、交换空间(swap)的使用情况等。这个文件是free命令的数据来源,通过查看meminfo文件,可以更深入地了解系统内存的分配和使用情况,判断系统是否存在内存不足或内存泄漏的问题。例如,当我们发现系统运行缓慢时,可以查看meminfo文件,检查内存使用情况,看是否是因为内存不足导致系统性能下降。

MemTotal:        16384000 kBMemFree:          2345672 kBMemAvailable:    12345678 kBBuffers:           123456 kBCached:           5678901 kBSwapCached:            0 kBActive:           8765432 kBInactive:         4567890 kBActive(anon):     4567890 kBInactive(anon):   1234567 kBActive(file):     4197542 kBInactive(file):   3333323 kBUnevictable:           0 kBMlocked:               0 kBSwapTotal:       2097148 kBSwapFree:        2097148 kBDirty:                 0 kBWriteback:             0 kBAnonPages:        5678901 kBMapped:           3456789 kBShmem:              1234 kBSlab:              789012 kBSReclaimable:      678901 kBSUnreclaim:        110111 kBKernelStack:        12345 kBPageTables:         34567 kBNFS_Unstable:          0 kBBounce:                0 kBWritebackTmp:          0 kBCommitLimit:     10289148 kBCommitted_AS:    11234567 kBVmallocTotal:   34359738367 kBVmallocUsed:      1234567 kBVmallocChunk:   34358498765 kBPercpu:             12345 kBHardwareCorrupted:     0 kBAnonHugePages:    1234567 kBShmemHugePages:        0 kBShmemPmdMapped:        0 kBCmaTotal:              0 kBCmaFree:               0 kBHugePages_Total:       0HugePages_Free:        0HugePages_Rsvd:        0HugePages_Surp:        0Hugepagesize:       2048 kBHugetlb:               0 kBDirectMap4k:      1234567 kBDirectMap2M:     10234567 kBDirectMap1G:      2097152 kB

从这些数据中,能清晰得知系统内存的总量、当前空闲内存量、各类缓存使用量以及交换空间的相关信息。

/proc/loadavg文件记录了系统的负载平均值,包括过去 1 分钟、5 分钟和 15 分钟内的平均负载。这个值反映了系统在一段时间内的繁忙程度,负载平均值越高,说明系统的任务队列越长,系统越繁忙。一般来说,理想的负载平均值应该小于 CPU 的核心数。例如,当发现系统负载平均值持续高于 CPU 核心数时,就需要关注系统的性能问题,可能需要对系统进行优化或调整任务分配。

2.79 2.99 3.03 5/349 3582467

上述输出中,前三个数值分别代表过去 1 分钟、5 分钟和 15 分钟的系统平均负载,第四个数表示当前运行队列中的进程数与系统最大进程数的比值,最后一个数是最后一个创建的进程 PID。

/proc/version文件则显示了当前内核的版本信息,包括内核版本号、编译时间、编译者以及使用的 GCC 版本等。这对于确定系统内核的具体版本,以及在进行内核相关的操作或排查问题时非常重要。例如,当需要为系统安装特定版本的内核模块时,就需要先查看version文件,确认内核版本是否匹配。

Linux version 5.15.0-58-generic (builduser@buildfarm) (gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)) #64-Ubuntu SMP Thu Jun 8 09:44:52 UTC 2023

从这个输出中,可以得知当前系统使用的内核版本是 5.15.0-58-generic,编译时间是 2023 年 6 月 8 日,编译者是 builduser,使用的 GCC 版本是 11.3.0。

接下来,重点讲解/proc/sys目录,它是内核参数动态调整的重要入口。在这个目录下,包含了众多可以动态修改的内核参数,通过修改这些参数对应的文件的值,可以在不重启系统的情况下,实时调整内核的行为,以满足不同的系统需求。

例如,当需要让服务器充当路由器,开启 IP 转发功能时,可以通过以下命令临时开启:

echo 1 > /proc/sys/net/ipv4/ip_forward

上述命令将/proc/sys/net/ipv4/ip_forward文件的值设置为 1,从而开启 IP 转发功能。此时,系统会立即生效这个设置,数据包将能够在不同的网络接口之间进行转发。

需要注意的是,通过修改/proc/sys目录下的文件来调整内核参数,这种方式是临时生效的,一旦系统重启,这些设置将会失效。如果需要永久生效,通常需要将相应的配置添加到/etc/sysctl.conf文件中,然后执行sysctl -p命令使配置生效。/proc/sys目录为我们提供了一种灵活、便捷的方式来动态调整内核参数,让我们能够根据系统的实际运行情况,对内核进行优化和定制。

二、 深挖内核:proc 文件系统的源码实现

2.1  proc 的内核初始化:从 proc_root_init 开始

基础篇咱们聊完了实操,接下来进入进阶篇——深挖内核源码,看看proc是怎么实现的。很多同学觉得源码难,其实咱们从初始化流程入手,结合源码片段,一步步看,一点都不复杂。proc的初始化,核心就一个函数:proc_root_init,它藏在fs/proc/root.c里,先上源码片段(内核5.15版本,最常用,注释我已经加上了):

#include<linux/fs.h>#include<linux/proc_fs.h>// proc初始化入口函数,通过fs_initcall宏,在内核启动阶段调用staticint __init proc_root_init(void){    // 1. 注册proc文件系统类型,让内核识别proc    proc_init_inodecache();    // 2. 创建/proc根目录,设置权限(所有人可读可执行)    proc_root = proc_mkdir_deprecated(""NULL);    if (!proc_root) {        printk(KERN_ERR "proc: failed to create root directory\n");        return -ENOMEM;    }    // 3. 初始化proc相关的默认文件(比如/proc/cpuinfo、/proc/meminfo)    proc_create_mount_point("cpuinfo", proc_root);    proc_create_mount_point("meminfo", proc_root);    // 其他初始化操作...    return 0;}// 内核启动时,自动调用proc_root_initfs_initcall(proc_root_init);

这段源码的逻辑很简单:

1.  内核启动时,会通过fs_initcall宏,自动调用proc_root_init,相当于给proc“上电”;

2.  函数第一步注册proc文件系统,告诉内核“这是proc虚拟文件系统,你要认识它”;

3.  创建/proc根目录,再初始化默认的核心文件,比如cpuinfomeminfo,这样咱们才能通过cat命令查看信息。

proc_root_init函数的核心逻辑并不复杂,但却至关重要。它的首要任务是注册 proc 文件系统类型,这一步骤使得内核能够识别并管理 proc 文件系统。通过调用proc_mkdir_deprecated函数,proc_root_init创建了 proc 文件系统的根目录/proc,并设置了其访问权限为S_IFDIR | S_IRUGO | S_IXUGO,这意味着该目录对所有者、组和其他用户都具有读和执行权限。这个根目录就像是 proc 文件系统这座大厦的基石,为后续各种文件和目录的挂载与创建奠定了基础。在根目录创建完成后,proc 文件系统便初步搭建起来,随时准备接收内核传递的各种系统信息,并以文件和目录的形式呈现给用户空间。

2.2  核心数据结构:proc_dir_entry 构建 proc 目录树

proc能形成咱们看到的目录树(比如/proc/1/proc/sys),核心靠一个数据结构:proc_dir_entry。你可以把它理解为“目录/文件的身份证”,每个proc的文件或目录,都有一个对应的proc_dir_entry实例,先上源码片段(简化版,保留核心成员,注释详解):

// 描述proc文件/目录的核心结构体(fs/proc/internal.h)struct proc_dir_entry {    const char      *name;       // 文件名/目录名(比如"meminfo"、"1")    umode_t         mode;        // 访问权限(比如0644,可读可写)    struct proc_dir_entry *parent;// 父目录指针(比如meminfo的父目录是/proc)    const struct proc_ops *proc_ops; // 操作函数集(读、写、打开等操作)    void            *data;       // 私有数据(传递给操作函数)    struct list_head children;   // 子目录/文件链表(管理下级节点)    // 其他次要成员...};

咱们结合/proc/meminfo举个例子,帮大家理解这个结构体的作用:

当内核初始化/proc/meminfo时,会创建一个proc_dir_entry实例,其中:name = "meminfo"parent = proc_root(父目录是/proc),proc_ops绑定了读操作函数(后面会讲),mode = 0444(所有人可读)。正是这个结构体,把meminfo/proc根目录关联起来,形成了proc的目录树。

proc_dir_entry结构体包含了许多重要的成员。其中,name成员存储了文件或目录的名称,这是我们在/proc目录下看到的文件名或目录名,通过它能够快速识别和定位对应的文件或目录。mode成员则定义了文件或目录的访问权限,决定了哪些用户可以对其进行读、写或执行操作,这与传统文件系统中的权限管理类似,确保了系统的安全性和数据的保密性。

parent指针指向该文件或目录的父目录的proc_dir_entry结构体,通过这个指针,proc 文件系统中的所有文件和目录形成了一个层次分明的树形结构,使得我们能够方便地进行目录遍历和查找操作。而proc_ops成员则是一个指向操作函数集的指针,这个函数集定义了对该文件或目录的各种操作,如读、写、打开、关闭等,它是实现 proc 文件系统动态数据交互和内核参数调整的关键所在。

/proc/cpuinfo文件为例,在内核中,它对应着一个proc_dir_entry结构体实例。当我们查看/proc/cpuinfo文件时,内核会根据这个实例中的proc_ops成员所指向的读操作函数,动态生成 CPU 信息并返回给用户。同样,对于/proc/sys/net/ipv4/ip_forward文件,当修改它的值时,内核会调用该实例proc_ops成员指向的写操作函数,从而实现对 IP 转发功能的开启或关闭。proc_dir_entry结构体通过其各个成员的协同工作,实现了 proc 文件系统的虚拟目录层级管理和动态数据交互功能,是 proc 文件系统实现的核心基石。

2.3  读写机制:proc_ops 驱动的动态数据交互

2.3.1  读操作:动态生成系统信息的 “幕后推手”

proc的核心价值的是“双向交互”——既能读系统信息,也能改内核参数,而这一切的底层,都是proc_ops操作函数集在驱动。先看读操作:咱们每次cat /proc/meminfo,内核都是怎么动态生成内容的?还是上源码,结合/proc/meminfo的实现(内核5.15,简化版,重点看核心逻辑):

#include<linux/proc_fs.h>#include<linux/seq_file.h>#include<linux/mm.h>// /proc/meminfo的读操作实现(核心函数)staticintmeminfo_proc_show(struct seq_file *m, void *v){    struct sysinfo i;    si_meminfo(&i); // 读取内核内存管理数据结构,获取实时内存信息    // 用seq_printf动态生成文件内容,和printf用法类似    seq_printf(m, "MemTotal:       %8lu kB\n", i.totalram >> (PAGE_SHIFT - 10));    seq_printf(m, "MemFree:        %8lu kB\n", i.freeram >> (PAGE_SHIFT - 10));    seq_printf(m, "Buffers:        %8lu kB\n", i.bufferram >> (PAGE_SHIFT - 10));    seq_printf(m, "Cached:         %8lu kB\n", (i.cached + i.sreclaimable) >> (PAGE_SHIFT - 10));    // 其他内存信息...    return 0;}// 绑定读操作函数(proc_ops结构体)static const struct proc_ops meminfo_proc_ops = {    .proc_open = single_open,    // 打开文件时调用    .proc_read = seq_read,       // 读取文件时调用(间接调用meminfo_proc_show)    .proc_lseek = seq_lseek,     // 光标移动时调用    .proc_release = single_release,// 关闭文件时调用};// 初始化时,将meminfo注册到/proc目录下staticint __init meminfo_init(void){    proc_create("meminfo"0444, proc_root, &meminfo_proc_ops);    return 0;}late_initcall(meminfo_init);

1.  当我们执行cat /proc/meminfo时,会触发meminfo_proc_ops里的proc_read(也就是seq_read);

2.  seq_read会调用meminfo_proc_show函数,这个函数通过si_meminfo(&i)读取内核内存的实时数据;

3.  再通过seq_printf把数据格式化成字符串,动态生成/proc/meminfo的内容,返回给用户。

这就是proc读操作的核心——没有固定文件内容,每次读取都由内核实时生成,这也是它“实时性”的底层原因。

/proc/meminfo文件为例,其读操作的实现源码位于fs/proc/meminfo.c文件中。在这个文件中,通过meminfo_proc_show函数读取内核内存管理数据结构,获取系统内存的各种信息,如总内存、空闲内存、缓存内存等。然后,利用seq_printf函数将这些信息按照特定的格式进行组织和格式化,动态生成/proc/meminfo文件的内容。每次执行cat /proc/meminfo命令时,都会触发proc_read函数(在这个例子中,proc_read函数关联的是seq_read,而seq_read会调用meminfo_proc_show函数),确保用户获取到的内存信息是实时更新的。这种动态生成文件内容的机制,使得 proc 文件系统能够实时反映系统的运行状态,为用户提供最新、最准确的系统信息。

2.3.2  写操作:用户空间修改内核参数的 “通道”

读操作是“内核给用户传信息”,而写操作则是“用户给内核传指令”——比如咱们修改/proc/sys/net/ipv4/ip_forward,就是通过写操作修改内核参数。同样,咱们结合源码和实操,看它的底层实现,以上面的IP转发为例:

#include<linux/proc_fs.h>#include<linux/sysctl.h>// ip_forward参数的写操作处理函数(简化版)staticssize_tip_forward_write(struct file *file, constchar __user *buf, size_t count, loff_t *ppos){    char kbuf[32];    int val, ret;    // 1. 将用户空间写入的数据,拷贝到内核空间(用户空间和内核空间不能直接通信)    if (copy_from_user(kbuf, buf, count)) {        return -EFAULT; // 拷贝失败,返回错误    }    // 2. 将字符串转换成整数(用户写入的是"1"或"0")    ret = kstrtoint(kbuf, 10, &val);    if (ret) {        return ret; // 转换失败,返回错误    }    // 3. 验证参数合法性(只能是0或1)    if (val != 0 && val != 1) {        return -EINVAL;    }    // 4. 修改内核全局参数(sysctl_ip_forward就是内核中控制IP转发的变量)    sysctl_ip_forward = val;    // 5. 刷新网络配置,让参数立即生效    net_post_set_sysctl(&net_ipv4_sysctl_ip_forward);    return count; // 返回写入的字节数,表示写入成功}// 绑定ip_forward的操作函数集static const struct proc_ops ip_forward_proc_ops = {    .proc_open = proc_sys_open,    .proc_read = seq_read,    .proc_write = ip_forward_write, // 写操作绑定上面的函数    .proc_lseek = seq_lseek,    .proc_release = proc_sys_release,};

结合实操命令echo 1 > /proc/sys/net/ipv4/ip_forward,咱们看整个流程:

1.  用户执行echo命令,将"1"写入该文件,触发proc_write(也就是ip_forward_write);

2.  函数先把用户写入的"1"从用户空间拷贝到内核空间,转换成整数1;

3.  验证参数合法后,修改内核全局变量sysctl_ip_forward为1;

4.  刷新网络配置,参数立即生效,无需重启系统。

这里要提醒大家:写操作虽然方便,但风险很高——如果写入非法参数(比如给ip_forward写2),会返回错误;但如果写入合法但不合理的参数(比如修改内存参数),可能导致系统崩溃,后面避坑指南会详细说。

以内核网络参数调整为例,当需要修改/proc/sys/net/ipv4/ip_forward文件的值来开启或关闭 IP 转发功能时,用户空间的程序会将新的值写入该文件。内核接收到写入的数据后,会调用proc_write函数,该函数会进一步调用对应的处理函数,对用户写入的数据进行解析和验证,然后根据数据修改内核中的相应参数。在这个过程中,proc_write函数就像是一座桥梁,连接着用户空间和内核空间,使得用户能够在不重启系统的情况下,实时调整内核的行为,满足不同的系统需求。

然而,需要注意的是,这种通过写入 proc 文件来修改内核参数的机制虽然方便,但也需要谨慎操作。因为错误的参数设置可能会导致系统异常,甚至影响系统的稳定性和安全性。所以,在进行内核参数调整时,一定要确保对参数的含义和影响有充分的了解,并且在必要时备份系统配置,以便在出现问题时能够及时恢复。proc_write函数为用户提供了强大的内核参数调优能力,但同时也要求用户具备一定的系统知识和操作经验,以确保系统的稳定运行。

三、 实战演练:proc 的运维调试与开发应用

3.1  运维排障实战:用 proc 解决性能问题

进阶篇的源码咱们聊完了,接下来进入实战篇——这部分全是干货,结合实际工作中的排障场景,带着完整命令和脚本,教大家用proc解决实际问题。运维和开发中,最常见的就是“系统卡顿、进程异常”,咱们分4个场景,逐个实操,不搞虚的。

场景1:系统响应缓慢,排查负载过高问题

# 1. 查看系统负载(判断是否过载)cat /proc/loadavg# 输出示例:2.79 2.99 3.03 5/349 3582467# 解读:前3个数字是1/5/15分钟负载,若大于CPU核心数(比如4核),就是过载# 2. 查看CPU核心数,对比负载cat /proc/cpuinfo | grep "cpu cores" | head -1# 3. 排查占用CPU最高的进程(核心脚本,直接复制使用)#!/bin/bashfor pid in /proc/[0-9]*/stat; do    # 读取进程用户态、内核态CPU时间    cpu_time=$(awk '{print $14 + $15}' $pid)    # 读取进程启动时间、系统运行时间    start_time=$(awk '{print $22}' $pid)    uptime=$(cat /proc/uptime | awk '{print $1}')    # 计算CPU使用率(保留2位小数)    cpu_usage=$(echo "scale=2; ($cpu_time / ($uptime - ($start_time / 100)) * 100)" | bc)    # 读取进程名    process_name=$(awk -F ' ' '{print $2}' $pid | tr -d '()')    # 输出进程名、PID、CPU使用率    echo "$process_name (PID: ${pid##*/stat}): $cpu_usage%"done | sort -nr -k 3 | head -10

把上面的脚本保存为proc_cpu_check.sh,执行bash proc_cpu_check.sh,就能快速找到占用CPU最高的10个进程,直接定位问题根源。

假设你负责维护一台运行着关键业务的 Linux 服务器,突然发现系统响应变得异常缓慢,用户频繁反馈业务卡顿。这时,你首先想到的可能是查看系统的负载情况,而 proc 文件系统中的/proc/loadavg文件就能提供这一关键信息。通过查看这个文件,你可以获取系统在过去 1 分钟、5 分钟和 15 分钟内的平均负载。如果这些数值持续高于系统的 CPU 核心数,那就说明系统负载过高,可能存在性能瓶颈。例如,当你看到/proc/loadavg文件中的数值为5.50 5.00 4.50,而你的服务器只有 4 个 CPU 核心,这就表明系统负载已经超出了正常范围,需要进一步排查原因。

接下来,你怀疑是某个进程占用了过多的 CPU 资源,导致系统整体性能下降。这时,proc 文件系统中以 PID 命名的进程专属目录就派上了用场。在/proc/[pid]/stat文件中,记录了进程的各种运行状态信息,其中utimestime字段分别表示进程在用户态和内核态运行的时间。通过计算这些时间与系统总运行时间的比例,你就可以准确地得知每个进程的 CPU 使用率。比如,你可以编写一个简单的脚本,遍历/proc目录下的所有 PID 目录,读取其中的stat文件,计算每个进程的 CPU 使用率,并按照使用率从高到低进行排序,这样就能快速找出占用 CPU 资源最多的进程。

#!/bin/bashfor pid in /proc/[0-9]*/stat; do    cpu_time=$(awk '{print $14 + $15}' $pid)    start_time=$(awk '{print $22}' $pid)    uptime=$(cat /proc/uptime | awk '{print $1}')    cpu_usage=$(echo "scale=2; ($cpu_time / ($uptime - ($start_time / 100)) * 100)" | bc)    process_name=$(awk -F ' ' '{print $2}' $pid | tr -d '()')    echo "$process_name ($pid): $cpu_usage%"done | sort -nr -k 3

运行这个脚本后,你会得到一个按照 CPU 使用率排序的进程列表,从而轻松定位到占用 CPU 资源过高的进程。

场景2:硬件中断异常,导致系统卡顿

# 1. 查看所有中断的统计信息(重点看中断次数是否异常)cat /proc/interrupts# 输出解读:第一列是中断号,后面是每个CPU的中断次数,最后是中断类型/设备# 2. 监控中断变化(每1秒刷新一次,看哪个中断次数快速增长)watch -n 1 "cat /proc/interrupts | grep -E 'eth0|irq'"# 3. 定位中断对应的设备(比如中断号123)grep 123 /proc/interrupts | awk '{print $NF}'# 若输出eth0,说明网卡中断异常,可能是网卡驱动问题或流量过大

场景3:进程占用过多文件资源,导致文件打开失败

# 1. 查看系统最大文件打开数限制cat /proc/sys/fs/file-max# 2. 查看当前系统已打开的文件总数cat /proc/sys/fs/file-nr# 3. 排查单个进程打开的文件数(比如PID=1234)ls -l /proc/1234/fd | wc -l# 4. 查看该进程打开的具体文件(定位异常文件)ls -l /proc/1234/fd | grep -E "log|txt|socket"

场景4:内存泄漏排查(判断是否有进程持续占用内存不释放)

# 1. 查看系统内存整体使用情况cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable"# 2. 排查单个进程内存占用(重点看VmRSS,实际物理内存占用)for pid in /proc/[0-9]*/status; do    pid_num=${pid##*/status}    process_name=$(grep "Name:" $pid | awk '{print $2}')    rss_mem=$(grep "VmRSS:" $pid | awk '{print $2}')    echo "$process_name (PID: $pid_num): $rss_mem kB"done | sort -nr -k 3 | head -10

这里再补充一个注意点:直接修改/proc文件的参数是临时生效的,重启就没了,永久生效需要修改/etc/sysctl.conf,后面避坑指南会详细说。

另外,当系统出现文件资源相关的问题时,proc 文件系统同样能发挥作用。假设你怀疑某个进程占用了大量的文件资源,导致系统文件打开失败或文件系统性能下降。这时,你可以查看/proc/[pid]/fd目录,这个目录下存放着进程打开的所有文件描述符的符号链接。通过统计这个目录下的文件数量,你可以大致了解进程打开的文件数量。同时,你还可以通过查看这些符号链接的目标,了解进程具体打开了哪些文件。比如,你可以使用ls -l /proc/[pid]/fd命令,查看每个文件描述符对应的文件路径,从而找出占用大量文件资源的进程,并进一步分析这些文件的使用情况,判断是否存在文件泄漏或不合理的文件打开操作。

在使用 proc 文件系统进行运维排障时,需要注意一些操作细节。直接修改/proc目录下的文件可能会对系统产生直接且即时的影响,因此在修改前一定要确保对修改的后果有充分的了解,并且最好在测试环境中进行验证。对于一些需要永久生效的配置,建议通过修改/etc/sysctl.conf文件来进行持久化设置,然后使用sysctl -p命令使配置生效。这样可以避免系统重启后配置丢失,同时也能保证系统的稳定性和可维护性。

3.2  内核开发实战:自定义 proc 文件的实现

3.2.1  案例:编写简单内核模块创建 proc 文件

聊完运维排障,再看内核开发中的实操——自定义proc文件。很多时候,我们开发内核模块,需要向用户空间暴露内核状态,或者让用户修改模块参数,这时候就需要自定义proc文件。咱们从零开始,写一个完整的内核模块,创建自定义proc文件,支持读和写,全程带代码、带测试,新手也能跟着编译运行。

第一步:编写内核模块代码(my_proc_module.c,内核5.15版本)

#include<linux/module.h>// 内核模块必备头文件#include<linux/proc_fs.h>// proc文件系统头文件#include<linux/seq_file.h>// 序列文件操作头文件#include<linux/uaccess.h>// 用户空间与内核空间数据拷贝头文件// 1. 定义proc_dir_entry指针(指向自定义的proc文件)static struct proc_dir_entry *my_proc_entry;// 2. 定义一个内核变量(用于演示:用户可以读、写这个变量)static int my_kernel_var = 0;// 3. 自定义proc文件的读操作实现(用户cat时调用)staticintmy_proc_show(struct seq_file *m, void *v){    // 向用户空间输出内核变量的值(类似printf)    seq_printf(m, "自定义proc文件:my_kernel_var = %d\n", my_kernel_var);    seq_printf(m, "使用方法:echo 数字 > /proc/my_proc_file (修改该变量)\n");    return 0;}// 4. 自定义proc文件的打开操作(绑定读操作函数)staticintmy_proc_open(struct inode *inode, struct file *file){    // single_open:简化的序列文件打开函数,绑定my_proc_show    return single_open(file, my_proc_show, NULL);}// 5. 自定义proc文件的写操作实现(用户echo时调用)staticssize_tmy_proc_write(struct file *file, constchar __user *buf, size_t count, loff_t *ppos){    char kbuf[32]; // 内核空间缓冲区(存储用户写入的数据)    // 限制写入长度,避免缓冲区溢出    if (count > sizeof(kbuf) - 1) {        count = sizeof(kbuf) - 1;    }    // 将用户空间的数据,拷贝到内核空间(copy_from_user:用户->内核)    if (copy_from_user(kbuf, buf, count)) {        return -EFAULT; // 拷贝失败,返回错误码    }    // 给字符串加结束符(用户写入的是没有结束符的字符串)    kbuf[count] = '\0';    // 将字符串转换成整数,赋值给内核变量    if (kstrtoint(kbuf, 10, &my_kernel_var)) {        return -EINVAL; // 转换失败(比如写入非数字),返回错误    }    // 打印内核日志(dmesg可以查看),用于调试    printk(KERN_INFO "my_proc: 内核变量已修改为:%d\n", my_kernel_var);    return count; // 返回写入的字节数,表示写入成功}// 6. 绑定proc文件的操作函数集(proc_ops结构体)static const struct proc_ops my_proc_ops = {    .proc_open = my_proc_open,    // 打开文件:my_proc_open    .proc_read = seq_read,        // 读取文件:seq_read(间接调用my_proc_show)    .proc_write = my_proc_write,  // 写入文件:my_proc_write    .proc_lseek = seq_lseek,      // 光标移动:默认序列文件操作    .proc_release = single_release,// 关闭文件:默认序列文件操作};// 7. 内核模块初始化函数(加载模块时调用)staticint __init my_proc_module_init(void){    // 创建自定义proc文件:文件名my_proc_file,权限0644(所有者可读可写,其他人可读)    my_proc_entry = proc_create("my_proc_file"0644NULL, &my_proc_ops);    // 判断创建是否成功    if (!my_proc_entry) {        printk(KERN_ERR "my_proc: 创建proc文件失败!\n");        return -ENOMEM; // 返回内存不足错误    }    printk(KERN_INFO "my_proc: 内核模块加载成功,proc文件已创建:/proc/my_proc_file\n");    return 0;}// 8. 内核模块卸载函数(卸载模块时调用)staticvoid __exit my_proc_module_exit(void){    // 删除自定义proc文件    proc_remove(my_proc_entry);    printk(KERN_INFO "my_proc: 内核模块卸载成功,proc文件已删除\n");}// 注册模块初始化和卸载函数module_init(my_proc_module_init);module_exit(my_proc_module_exit);// 模块许可证(必须有,否则加载失败)MODULE_LICENSE("GPL");// 模块描述MODULE_DESCRIPTION("自定义proc文件示例模块");// 模块作者MODULE_AUTHOR("公众号:Linux教程");

第二步:编写Makefile(用于编译内核模块)

# 定义要编译的内核模块(my_proc_module.c -> my_proc_module.ko)obj-m += my_proc_module.o# 定义内核源码目录(自动获取当前系统内核版本的源码目录)KERNELDIR ?= /lib/modules/$(shell uname -r)/build# 当前目录(模块源码所在目录)PWD := $(shell pwd)# 编译规则:进入内核源码目录,编译当前目录的模块all:    make -C $(KERNELDIR) M=$(PWD) modules# 清理规则:清理编译生成的文件clean:    make -C $(KERNELDIR) M=$(PWD) clean

我们以创建一个简单的内核模块并在其中创建一个 proc 文件为例,逐步讲解开发过程。首先,需要定义一个proc_dir_entry指针,用于指向要创建的 proc 文件。这个指针就像是一个文件的 “标识”,通过它可以对 proc 文件进行各种操作。

#include<linux/module.h>#include<linux/proc_fs.h>#include<linux/seq_file.h>static struct proc_dir_entry *my_proc_file;

接下来,使用proc_create函数来创建 proc 文件。这个函数的参数包括文件名、文件权限、父目录以及操作函数集。文件名就是我们希望在/proc目录下看到的文件名称,文件权限决定了哪些用户可以对该文件进行读、写等操作,父目录指定了该文件在/proc目录树中的位置,而操作函数集则定义了对该文件的各种操作,如读、写、打开、关闭等。以下是创建 proc 文件的代码示例:

staticintmy_proc_open(struct inode *inode, structfile *file) {    return single_open(file, my_proc_show, NULL);}staticintmy_proc_show(struct seq_file *m, void *v) {    seq_printf(m, "This is a custom proc file.\n");    return 0;}static const struct proc_ops my_proc_ops = {   .proc_open = my_proc_open,   .proc_read = seq_read,   .proc_lseek = seq_lseek,   .proc_release = single_release,};static int __initmy_module_init(void) {    my_proc_file = proc_create("my_proc_file"0644, NULL, &my_proc_ops);    if (!my_proc_file) {        printk(KERN_ERR "Failed to create proc file\n");        return -ENOMEM;    }    return 0;}

在上述代码中,my_proc_open函数用于初始化文件打开操作,它通过single_open函数将文件的读取操作关联到my_proc_show函数。my_proc_show函数则负责生成 proc 文件的内容,这里我们简单地返回了一个固定的字符串。my_proc_ops结构体定义了对 proc 文件的操作集,将各个操作函数与文件操作进行关联。在my_module_init函数中,我们调用proc_create函数创建了名为my_proc_file的 proc 文件,并设置了其权限为 0644,即所有者可读可写,组和其他用户可读。

需要注意的是,在不同的内核版本中,proc 文件系统的实现可能会有一些差异。在 Linux 内核 5.6 之前,proc 文件的操作函数集通常使用struct file_operations结构体来定义,而从内核 5.6 开始,推荐使用struct proc_ops结构体。这一变化主要是为了更好地统一和管理 proc 文件系统的操作,提高代码的可读性和可维护性。在进行内核开发时,一定要根据目标内核版本的特性来编写代码,确保代码的兼容性和正确性。

3.2.2  测试验证:读写自定义 proc 文件

第三步:编译、加载模块,测试自定义proc文件

# 1. 编译内核模块(在源码和Makefile所在目录执行)make# 编译成功后,会生成my_proc_module.ko文件(内核模块文件)# 2. 加载内核模块(需要root权限,sudo)sudo insmod my_proc_module.ko# 3. 查看模块是否加载成功(两种方法)# 方法1:查看内核日志(查看加载信息)dmesg | tail -5# 成功输出:[timestamp] my_proc: 内核模块加载成功,proc文件已创建:/proc/my_proc_file# 方法2:查看已加载的内核模块lsmod | grep my_proc_module# 4. 测试proc文件的读操作(cat查看内容)cat /proc/my_proc_file# 预期输出:# 自定义proc文件:my_kernel_var = 0# 使用方法:echo 数字 > /proc/my_proc_file (修改该变量)# 5. 测试proc文件的写操作(echo修改内核变量)sudo echo 100 > /proc/my_proc_file# 6. 再次读取,验证修改是否生效cat /proc/my_proc_file# 预期输出:# 自定义proc文件:my_kernel_var = 100# 使用方法:echo 数字 > /proc/my_proc_file (修改该变量)# 7. 查看内核日志,确认内核变量已修改dmesg | tail -1# 预期输出:[timestamp] my_proc: 内核变量已修改为:100# 8. 卸载内核模块(测试完成后,清理模块)sudo rmmod my_proc_module.ko# 9. 验证proc文件已删除ls /proc/my_proc_file# 预期输出:ls: 无法访问 '/proc/my_proc_file': 没有那个文件或目录

这里补充2个常见问题,避免大家踩坑:

1.  编译失败:提示“没有那个文件或目录”,大概率是没有安装内核源码,执行sudo apt install linux-headers-$(uname -r)(Ubuntu)或sudo yum install kernel-devel-$(uname -r)(CentOS)安装即可;

2.  写入失败:提示“权限不够”,因为proc文件权限是0644,普通用户没有写权限,必须加sudo;

3.  内核版本兼容:本文用的是内核5.15版本,若你的内核版本低于5.6,需要将struct proc_ops改为struct file_operations,并修改对应函数名(比如proc_open改为open),后面会补充兼容代码。

通过这个案例,大家就能明白:自定义proc文件的核心,就是创建proc_dir_entry实例,绑定proc_ops操作函数集,实现读、写逻辑,从而实现内核空间和用户空间的双向通信。

obj-m += my_module.oall:    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在这个 Makefile 中,obj-m += my_module.o表示我们要将my_module.c文件编译成可动态加载的内核模块my_module.koall目标定义了编译的规则,通过make -C命令切换到内核源码目录,然后使用M=$(PWD)指定当前目录为模块源码目录,最后执行modules目标进行模块编译。clean目标则用于清理编译生成的文件,确保每次编译都是全新的。

编译完成后,可以使用insmod命令将内核模块加载到内核中:

sudo insmod my_module.ko

加载成功后,就可以在/proc目录下看到我们创建的my_proc_file文件。接下来,可以使用cat命令读取这个文件的内容:

cat /proc/my_proc_file

如果一切正常,你将会看到输出的内容为:This is a custom proc file.,这正是我们在my_proc_show函数中定义的内容。

为了进一步验证 proc 文件的功能,可以尝试向文件中写入数据。首先,需要在内核模块中实现proc_write函数,用于处理写入操作。以下是修改后的代码示例:

static ssize_t my_proc_write(structfile *fileconstchar __user *buf, size_t count, loff_t *ppos) {    char user_buf[1024];    if (count > sizeof(user_buf) - 1) {        count = sizeof(user_buf) - 1;    }    if (copy_from_user(user_buf, buf, count)) {        return -EFAULT;    }    user_buf[count] = '\0';    printk(KERN_INFO "Received data: %s\n", user_buf);    return count;}static const struct proc_ops my_proc_ops = {   .proc_open = my_proc_open,   .proc_read = seq_read,   .proc_write = my_proc_write,   .proc_lseek = seq_lseek,   .proc_release = single_release,};

在上述代码中,my_proc_write函数首先从用户空间缓冲区buf中读取数据,并将其存储到内核空间的user_buf中。然后,它将接收到的数据打印到内核日志中,以便我们查看。最后,返回实际写入的数据长度。

重新编译并加载内核模块后,可以使用echo命令向my_proc_file文件中写入数据:

echo "Hello, proc!" > /proc/my_proc_file

写入完成后,可以查看内核日志,验证数据是否被正确接收。在终端中输入以下命令查看内核日志:

dmesg | tail

如果一切正常,你将会看到类似以下的输出:[timestamp] Received data: Hello, proc!,这表明成功地向自定义 proc 文件中写入了数据,并在内核中正确地接收到了这些数据。通过以上步骤,完成了从自定义 proc 文件的内核模块编写到测试验证的整个过程,充分展示了 proc 文件系统在 Linux 内核开发中的强大功能和灵活性。

四、 避坑指南:proc 使用的三大注意事项

4.1  参数修改风险:谨慎操作 /proc/sys

实操多了就会发现,proc最容易踩坑的地方,就是/proc/sys目录下的参数修改——看似简单的echo命令,一旦输错参数,可能直接导致系统崩溃。咱们结合实际工作中最常见的踩坑场景,带命令、带解决方案,帮大家避坑。

坑点1:修改内存参数,导致系统OOM(内存溢出)

# 错误操作:将swappiness设置为0(禁止内存交换),内存不足时触发OOMsudo echo 0 > /proc/sys/vm/swappiness# 正确操作:先查看当前参数,备份后再修改,设置合理值(10-60)# 1. 查看当前swappiness值sysctl vm.swappiness# 2. 临时修改(测试,重启失效)sudo echo 10 > /proc/sys/vm/swappiness# 3. 永久修改(推荐,重启生效)sudo echo "vm.swappiness = 10" >> /etc/sysctl.conf# 4. 使永久配置生效sudo sysctl -p

坑点2:修改网络参数,导致网络中断

# 错误操作:误修改tcp_max_syn_backlog为0,导致无法建立TCP连接sudo echo 0 > /proc/sys/net/ipv4/tcp_max_syn_backlog# 解决方案:# 1. 临时恢复默认值(不同系统默认值不同,一般是128或512)sudo echo 128 > /proc/sys/net/ipv4/tcp_max_syn_backlog# 2. 查看系统默认值(参考)cat /proc/sys/net/ipv4/tcp_max_syn_backlog.default  # 部分系统支持# 3. 永久配置,写入/etc/sysctl.confsudo echo "net.ipv4.tcp_max_syn_backlog = 128" >> /etc/sysctl.confsudo sysctl -p

核心避坑原则:修改/proc/sys参数前,务必做2件事——1. 用sysctl查看当前参数,记录备份;2. 先临时修改,测试无误后,再永久写入/etc/sysctl.conf,避免重启后配置丢失或异常。

以内存参数/proc/sys/vm/swappiness为例,它用于控制内核将内存页交换到磁盘交换空间(swap)的倾向,取值范围是 0 - 100。如果将其值设置得过高,比如设置为 90,系统会频繁地将内存页交换到磁盘,这会大大增加磁盘 I/O 的负担,导致系统性能急剧下降。因为磁盘的读写速度远远低于内存,频繁的磁盘 I/O 操作会使系统响应变得迟缓,应用程序的运行速度也会受到严重影响。

相反,如果将swappiness设置为 0,虽然可以避免内存页被交换到磁盘,减少磁盘 I/O 操作,但在内存不足的情况下,系统可能会因为无法释放内存而出现 OOM(Out Of Memory)错误,导致进程被系统强制杀死,严重时甚至会使整个系统崩溃。

为了避免因参数修改而带来的风险,在进行参数调整之前,务必备份当前的参数值。可以使用sysctl命令来查看和备份参数,例如,查看swappiness的当前值并记录下来:

sysctl vm.swappiness

如果需要修改参数,建议优先通过/etc/sysctl.conf文件进行持久化配置。在这个文件中添加或修改相应的参数,然后执行sysctl -p命令使配置生效。这样做的好处是,系统重启后配置依然有效,而且可以避免因临时修改而导致的配置丢失或误操作。例如,将swappiness设置为 10,可以在/etc/sysctl.conf文件中添加如下配置:

vm.swappiness = 10

然后执行sysctl -p使配置生效。这样,即使在修改过程中出现问题,也可以通过恢复sysctl.conf文件中的原始配置来解决,确保系统的稳定性和可靠性。

4.2  权限控制:限制敏感文件的访问

proc下很多文件包含敏感信息,比如/proc/[pid]/cmdline可能包含密码、密钥(比如启动进程时传入的密码参数),/proc/[pid]/mem能查看进程内存数据,一旦被非授权用户访问,会造成信息泄露。咱们结合实操,讲2种最常用的权限控制方法,简单易上手。

# 方法1:修改文件权限(最直接,适合临时控制)# 以PID=1234的进程为例,限制只有root能读取cmdlinesudo chmod 400 /proc/1234/cmdline# 验证权限:普通用户读取,会提示权限不够cat /proc/1234/cmdline# 预期输出:cat: /proc/1234/cmdline: 权限不够# 恢复权限(需要时)sudo chmod 444 /proc/1234/cmdline

方法2:结合SELinux(生产环境推荐,强制访问控制,更安全)

SELinux是Linux的强制访问控制模块,能实现更细粒度的权限控制,比如限制某个进程只能访问自己的proc文件,步骤如下(以Ubuntu/CentOS为例):

# 1. 检查SELinux状态(确保已启用)sestatus# 若显示SELinux status: enabled,说明已启用;若未启用,执行sudo setenforce 1临时启用# 2. 创建SELinux策略模块(限制myapp进程访问其他proc文件)# 新建策略文件myapp_proc.tecat > myapp_proc.te << EOFmodule myapp_proc 1.0;require {    type myapp_t;       # 应用程序的SELinux类型(需根据实际修改)    type proc_t;        # proc文件系统的SELinux类型    class file { read }; # 限制的操作(只读)}# 允许myapp进程读取自己的proc文件allow myapp_t self:file { read };# 禁止myapp进程读取其他进程的proc文件neverallow myapp_t proc_t:file { read };EOF# 3. 编译、加载SELinux策略模块checkmodule -M -m -o myapp_proc.mod myapp_proc.tesemodule_package -o myapp_proc.pp -m myapp_proc.modsudo semodule -i myapp_proc.pp# 4. 验证策略是否生效# 切换到myapp用户,尝试读取其他进程的proc文件su - myappcat /proc/1/cmdline# 预期输出:Permission denied(权限被拒)

补充说明:myapp_t是应用程序的SELinux类型,不同应用类型不同,可以用ps -eZ | grep myapp查看,替换成实际类型即可。生产环境中,建议结合两种方法,既限制文件权限,又启用SELinux,双重保障敏感信息安全。

为了防止这种情况的发生,需要通过文件权限来限制非 root 用户对这些敏感文件的访问。在 Linux 系统中,可以使用chmod命令来修改文件的权限。例如,将/proc/[pid]/cmdline文件的权限设置为只有 root 用户可读:

sudo chmod 400 /proc/[pid]/cmdline

这样,除了 root 用户外,其他用户将无法读取该文件的内容,从而保护了敏感信息的安全。

在生产环境中,仅依靠文件权限控制可能还不够,还可以结合SELinux(Security-Enhanced Linux)等安全机制来进一步加强权限管控。SELinux是一种强制访问控制(MAC)的安全模块,它通过定义一系列的安全策略,对系统中的进程、文件等资源进行更细粒度的访问控制。例如,通过SELinux的策略配置,可以限制某个进程只能访问特定的文件和目录,即使该进程拥有较高的权限,也无法突破SELinux的访问限制,从而有效防止了敏感信息的泄露。

假设有一个名为myapp的应用程序,其进程 ID 为1234,我们希望限制myapp进程对/proc/1234/cmdline文件的访问,使其只能读取自己的cmdline文件,而无法访问其他进程的cmdline文件。在SELinux中,可以通过以下步骤实现:

1.首先,确认SELinux已经安装并启用。可以通过以下命令检查SELinux的状态:

sestatus

如果SELinux status显示为enabled,则表示SELinux已经启用。 

2. 然后,创建一个新的SELinux策略模块。可以使用sepolicy module工具来创建,例如:

module myapp_proc_perms 1.0;require {    type myapp_t;    type proc_t;    classfileread };}# 允许myapp_t类型的进程读取自己的proc文件allow myapp_t self:file { read };# 禁止myapp_t类型的进程读取其他进程的proc文件neverallow myapp_t proc_t:file { read };

将上述内容保存为一个文件,例如myapp_proc_perms.te

3. 接着,编译和加载这个策略模块:

checkmodule -M -m -o myapp_proc_perms.mod myapp_proc_perms.tesemodule_package -o myapp_proc_perms.pp -m myapp_proc_perms.modsudo semodule -i myapp_proc_perms.pp

通过以上步骤,就可以利用SELinux的策略来限制myapp进程对/proc文件系统中敏感文件的访问,进一步提升系统的安全性。

4.3  性能影响:避免频繁读取大文件

很多同学写运维脚本时,会频繁读取/proc下的大文件,比如/proc/kallsyms(内核符号表,几十MB)、/proc/vmallocinfo(虚拟内存信息),殊不知这样会严重消耗CPU——因为每次读取,内核都要重新生成这些大文件的内容,频繁读取会让CPU使用率飙升。咱们结合实操,讲问题排查和优化方法。

# 问题演示:频繁读取/proc/kallsyms,导致CPU升高# 编写一个错误的脚本(每10秒读取一次kallsyms)#!/bin/bashwhile truedo    cat /proc/kallsyms > /dev/null  # 读取kallsyms,丢弃输出    sleep 10done# 执行脚本后,查看CPU使用率top -p $(pgrep -f "cat /proc/kallsyms")# 会发现该进程的CPU使用率持续在10%以上,长期运行会拖慢系统

优化方案:合理控制读取频率,增加缓存机制,避免重复读取,优化后的脚本如下:

# 优化后的脚本(每5分钟读取一次,缓存到文件,避免频繁触发内核生成)#!/bin/bashCACHE_FILE="/tmp/proc_kallsyms_cache"  # 缓存文件路径CACHE_TIME=300                          # 缓存有效期(5分钟,300秒)# 定义读取kallsyms的函数read_kallsyms() {    cat /proc/kallsyms > $CACHE_FILE  # 读取一次,写入缓存文件    echo $(date +%s) > ${CACHE_FILE}.time  # 记录缓存时间}# 检查缓存是否过期,过期则重新读取if [ ! -f $CACHE_FILE ] || [ $(($(date +%s) - $(cat ${CACHE_FILE}.time 2>/dev/null))) -gt $CACHE_TIME ]; then    read_kallsymsfi# 后续需要使用kallsyms内容时,直接读取缓存文件,不访问/proccat $CACHE_FILE  # 使用缓存,避免频繁读取/proc

另外,教大家一个快速排查的方法——用perf工具,定位因读取proc大文件导致的CPU升高问题:

# 1. 安装perf工具sudo apt install linux-tools-common  # Ubuntusudo yum install perf                # CentOS# 2. 监控CPU使用率,定位异常进程sudo perf top# 3. 若发现seq_read、proc_read等函数占用CPU过高,说明频繁读取proc文件# 4. 查看该进程的具体操作,定位到读取的proc文件sudo perf trace -p 进程PID | grep "read" | grep "/proc"

核心优化原则:对于/proc/kallsyms/proc/vmallocinfo等大文件,尽量减少读取频率(比如5-10分钟一次),并使用缓存机制,避免重复触发内核生成文件内容;对于小文件(比如/proc/loadavg),读取频率也不宜过高(建议10秒以上),减少内核负担。

想象一下,有一个运维脚本需要定期监控系统状态,其中包含了频繁读取/proc/kallsyms文件的操作。每次读取这个文件时,内核都需要遍历内核符号表,生成相应的文件内容,这是一个相对耗时的操作。如果这个脚本每分钟执行一次,每次都读取/proc/kallsyms文件,随着时间的推移,CPU 的使用率会逐渐升高,导致系统的整体性能下降。尤其是在系统负载较高的情况下,这种频繁读取大文件的操作可能会成为系统性能的瓶颈,影响其他关键业务的正常运行。

为了避免这种情况,在编写运维脚本或其他需要读取/proc文件的程序时,应合理控制读取这些大文件的频率。可以根据实际需求,减少不必要的读取操作,或者增加缓存机制,避免重复读取相同的文件内容。例如,可以在脚本中设置一个变量,记录上次读取/proc/kallsyms文件的时间,只有当距离上次读取时间超过一定阈值时,才再次读取该文件,这样可以有效减少内核的负担,提高系统的性能。同时,在进行性能优化时,也可以使用perf等性能分析工具,定位到因频繁读取/proc大文件而导致的性能问题,然后针对性地进行优化。

五、 proc 之外的虚拟文件系统

在Linux的虚拟文件系统家族中,proc并非唯一的存在。与proc文件系统类似,sysfs 和 debugfs 也是非常重要的虚拟文件系统:

虚拟文件系统挂载目录核心用途特点
proc/proc查看系统/进程实时信息、修改内核参数实时性强、双向交互、侧重系统/进程监控
sysfs/sys管理设备、驱动、总线信息结构清晰、规范,侧重设备管理
debugfs/sys/kernel/debug内核调试、导出内核调试信息灵活、仅供开发调试,默认不启用

sysfs 文件系统主要用于管理设备和驱动程序,以及其他内核子系统的状态。它通过一个层次化的目录结构,将设备、驱动程序、总线等内核对象以文件和目录的形式呈现给用户空间。与 proc 文件系统相比,sysfs 的结构更加清晰、规范,更适合用于设备信息的展示和管理。例如,在 sysfs 中,所有的设备都按照其所属的总线类型,被组织在/sys/bus目录下,每个设备都有对应的设备目录,其中包含了设备的各种属性文件,如设备名称、型号、驱动程序等。这使得用户空间的程序能够方便地获取设备的详细信息,进行设备配置和管理。

debugfs 则是一个专为内核开发人员设计的虚拟文件系统,主要用于导出内核数据结构和调试信息。它为内核开发人员提供了一种简单而强大的方式,来实时访问和修改内核内部的数据结构,查看调试信息,定位和解决内核中的问题。与 proc 文件系统相比,debugfs 更加专注于内核调试,其文件和目录的组织方式更加灵活,通常根据具体的调试需求来创建和使用。例如,在内核开发过程中,开发人员可以通过 debugfs 创建自定义的调试文件,将内核中的变量、数据结构等信息导出到用户空间,以便进行分析和调试。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 22:02:15 HTTP/2.0 GET : https://f.mffb.com.cn/a/469077.html
  2. 运行时间 : 0.098084s [ 吞吐率:10.20req/s ] 内存消耗:4,807.55kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=d5281ec4a52bd9664e46805eab6a1fcb
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000522s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000869s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.001110s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000407s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000525s ]
  6. SELECT * FROM `set` [ RunTime:0.000181s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000556s ]
  8. SELECT * FROM `article` WHERE `id` = 469077 LIMIT 1 [ RunTime:0.005669s ]
  9. UPDATE `article` SET `lasttime` = 1770472936 WHERE `id` = 469077 [ RunTime:0.005246s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000358s ]
  11. SELECT * FROM `article` WHERE `id` < 469077 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000471s ]
  12. SELECT * FROM `article` WHERE `id` > 469077 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000370s ]
  13. SELECT * FROM `article` WHERE `id` < 469077 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001205s ]
  14. SELECT * FROM `article` WHERE `id` < 469077 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008648s ]
  15. SELECT * FROM `article` WHERE `id` < 469077 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.006488s ]
0.099706s