程序是如何启动的(Linux平台)

程序是如何启动的

程序是如何启动的(Linux平台)

Linux平台下的可执行程序以ELF(Executable and Linkable Format)格式存储于磁盘,启动的核心本质是将ELF文件从磁盘加载至内存,完成进程初始化与指令执行;程序退出则是反向流程,核心是终止指令执行、彻底回收系统资源,避免资源泄漏。整个流程涉及系统调用、内存管理、进程调度、动态链接等核心机制。本文将按步骤拆解Linux平台下可执行程序的启动及退出流程。

步骤1:触发启动指令(用户态触发与系统调用)

程序启动的触发源于用户操作,本质是通过系统调用向内核发起进程创建请求,常见触发方式及底层逻辑如下:

– 终端启动:通过shell(bash、zsh等)输入可执行程序路径(如./test、/usr/bin/ls),shell解析路径后调用exec系列系统调用(如execve),发起程序启动请求;

– 图形界面启动:双击桌面图标(本质是.desktop文件),桌面环境(如GNOME、KDE)解析.desktop文件中的Exec字段,获取程序路径,调用execve系统调用触发启动;

– 其他触发方式:通过进程间通信(IPC,如管道、信号)、服务启动(systemctl start 服务名)、调试器(如gdb)附加启动,本质均是通过exec系列系统调用触发ELF文件加载。

核心要点:所有启动方式最终都会映射到execve系统调用(内核态入口为sys_execve),execve会替换当前进程的地址空间(若由shell启动,shell进程会先调用fork创建子进程,再在子进程中执行execve,避免shell进程被替换);若启动时需要提升权限(如sudo启动),会触发setuid/setgid校验,通过后以目标用户(如root)权限启动进程。

步骤2:ELF文件定位与路径解析

系统接收到execve系统调用后,首要任务是定位目标ELF文件,完成路径解析与初步校验,核心流程如下:

1. 路径解析:若输入的程序路径为相对路径(如./test),系统会结合当前工作目录(cwd)拼接完整路径;若为绝对路径(如/usr/bin/ls),直接定位磁盘文件;若未指定路径(如ls),系统会按环境变量PATH的顺序,遍历所有指定目录,查找对应的ELF文件;

2. 初步校验:确认文件存在且具有可执行权限(用户/组/其他用户的x权限,通过stat系统调用获取文件权限位),排除非可执行文件、无权限文件;同时校验文件魔数(ELF文件魔数为0x7f454c46,即“\x7fELF”),确认是合法ELF格式文件。

核心要点:路径解析依赖环境变量PATH、PWD等,环境变量由父进程继承(如shell启动程序,会继承shell的环境变量);若路径解析失败(如文件不存在)或无执行权限,execve会返回-1,启动流程终止,shell会提示“command not found”或“Permission denied”。

步骤3:ELF文件合法性与安全性校验(内核态校验)

定位到ELF文件后,内核会在sys_execve函数中完成ELF文件的合法性与安全性校验,避免恶意文件、损坏文件启动,核心校验内容如下:

1. ELF文件完整性校验:解析ELF文件头(Elf32_Ehdr/Elf64_Ehdr)、程序头表(Elf32_Phdr/Elf64_Phdr),校验文件结构是否完整,是否存在文件截断、篡改等问题;

2. 权限与安全校验:校验ELF文件的setuid/setgid位,若设置了setuid位,启动后进程的有效用户ID(euid)会变为文件所有者ID(如root),执行完核心逻辑后需手动降权,避免权限滥用;同时结合selinux/apparmor安全策略,检测文件是否符合系统安全规则;

3. 动态链接校验:若为动态链接ELF文件(依赖ld.so动态链接器),校验是否存在动态链接器路径(ELF文件头中指定的INTERP段),若缺失动态链接器,会返回启动失败。

补充说明:第三方安全工具(如AppArmor、SELinux)会额外拦截校验过程,对可疑ELF文件(如无签名、异常权限)进行拦截,终止启动流程;校验失败则execve返回错误码,启动终止。

步骤4:进程创建与系统资源分配

ELF文件校验通过后,内核会创建新的进程,为程序运行分配必要的系统资源,核心操作如下:

1. 进程创建:内核调用do_fork函数(sys_fork的底层实现),创建进程控制块(PCB,即task_struct结构体),分配进程ID(PID)、线程ID(TID,Linux中进程与线程本质是task_struct,线程为轻量级进程,共享进程地址空间);设置进程状态为“就绪”(TASK_RUNNING),等待CPU调度;

2. 地址空间分配:通过mm_struct结构体创建进程专属的虚拟地址空间,划分代码段(.text)、数据段(.data/.bss)、堆、栈、共享库区域等,其中栈初始化为指定大小(默认由系统配置,可通过ulimit调整),堆用于程序运行时动态申请内存;

3. 资源分配与继承:进程继承父进程的文件描述符表(管理打开的内核对象,如文件、管道)、环境变量、信号掩码等;内核为进程分配文件描述符0(标准输入)、1(标准输出)、2(标准错误),默认关联终端设备;

4. 动态链接器加载:若为动态链接ELF文件,内核会加载ELF文件中INTERP段指定的动态链接器(如/lib64/ld-linux-x86-64.so.2),将动态链接器加载至进程虚拟地址空间,由动态链接器负责后续ELF加载与依赖解析。

核心要点:Linux中“进程是task_struct的集合”,线程(轻量级进程)与进程共享mm_struct(虚拟地址空间),仅拥有独立的栈和寄存器;资源分配以进程为单位,调度以task_struct为单位。

步骤5:ELF文件加载与动态链接解析

进程与资源分配完成后,由动态链接器(ld.so)主导,完成ELF文件加载与依赖解析,核心流程如下:

1. ELF文件映射:通过mmap系统调用,将ELF文件的代码段、数据段等从磁盘映射至进程虚拟地址空间(采用内存映射机制,提升加载效率,避免一次性读取整个文件);根据程序头表(Phdr)中的权限设置,为各段设置虚拟内存权限(如代码段为只读可执行,数据段为可读可写);

2. 动态依赖解析:遍历ELF文件的动态段(.dynamic),解析依赖的共享库(.so文件),若共享库存在依赖链(如liba.so依赖libb.so),会递归加载所有依赖共享库;动态链接器维护共享库的引用计数,每加载一次计数加1,卸载一次减1,计数为0时彻底释放内存;

3. 重定位与符号解析:通过ELF重定位表(.rela.text/.rela.data),完成代码段、数据段的重定位,解决绝对地址偏移问题,确保指令能正确执行;解析ELF符号表(.dynsym),将共享库中导出函数的地址填充至程序的导入符号表,确保程序能正常调用共享库函数;

4. 静态链接补充:若为静态链接ELF文件(不依赖共享库),会将所有依赖的代码、数据整合至自身,无需加载动态链接器,直接完成ELF映射与重定位,启动速度更快,但程序体积更大。

核心要点:动态链接器(ld.so)是动态链接ELF启动的核心,负责共享库加载、符号解析、重定位等操作;静态链接与动态链接的核心区别的是“是否依赖外部共享库”,静态链接可独立运行,动态链接依赖共享库存在。

步骤6:主线程启动与程序入口执行

ELF文件加载与动态链接完成后,内核调度主线程(进程的初始线程)启动,执行程序核心逻辑,流程如下:

1. 线程调度:CPU调度器(CFS调度器,完全公平调度器)根据进程优先级(nice值),将主线程从“就绪”状态切换为“运行”状态,加载线程寄存器上下文(如程序计数器PC,指向ELF入口地址);

2. 入口执行:ELF文件头中指定的入口地址(e_entry)为程序启动入口,对于C/C++编写的程序,入口并非用户编写的main函数,而是动态链接器初始化后的_start函数(由glibc提供);

3. 程序初始化:_start函数会完成glibc初始化、全局变量/静态变量初始化、线程局部存储(TLS)初始化、标准输入/输出流初始化等操作,调用main函数,执行用户编写的核心逻辑;若为图形界面程序,会加载对应的图形库(如GTK+),创建窗口并显示,启动完成。

补充缺失点:_start函数执行前,动态链接器会完成PLT(过程链接表)与GOT(全局偏移表)的修复,将共享库函数的占位地址替换为实际地址;初始化完成后,若程序注册了初始化函数(如constructor属性修饰的函数),会先执行该类函数,再进入main函数。

步骤7:进程运行与系统监控

程序启动完成后进入运行状态,内核与系统会全程监控进程运行,核心操作如下:

– 进程调度:CFS调度器根据进程nice值(优先级),动态分配CPU时间片,实现多进程、多线程并发运行;线程可通过pthread_create创建,与主线程共享进程地址空间,仅拥有独立栈和寄存器;

– 异常处理:若程序出现异常(如内存访问越界、除零错误),会触发信号(如SIGSEGV、SIGFPE),若程序未注册自定义信号处理函数,内核会执行默认处理(终止进程并生成核心转储文件core dump);

– 资源管理:进程可通过brk、mmap等系统调用动态申请/释放虚拟内存,内核会根据物理内存使用情况,进行页面置换(LRU算法),确保进程正常运行;同时监控文件描述符使用,避免句柄泄漏。

补充缺失点:运行过程中,内核会通过task_struct实时记录进程状态(运行、就绪、睡眠等),若进程调用sleep、wait等函数,会切换为睡眠状态(TASK_INTERRUPTIBLE/TASK_UNINTERRUPTIBLE),等待事件触发后重新进入就绪状态。

步骤8:程序退出流程(核心操作与资源回收)

程序退出是启动流程的反向操作,核心目标是终止指令执行、彻底回收所有系统资源,避免资源泄漏,分为“正常退出”和“异常退出”两种场景,底层操作统一且严谨,具体步骤如下:

1. 触发退出指令(两种场景):

– 正常退出:由用户主动操作(如终端输入Ctrl+C、点击图形界面关闭按钮)或程序自身逻辑触发(如main函数执行完毕返回),最终调用exit(用户态)或_exit(内核态)系统调用,发起退出请求;

– 异常退出:程序运行中出现未处理信号(如SIGSEGV内存崩溃、SIGKILL强制终止)、断言失败,或被其他进程通过kill系统调用终止,由内核触发exit_group系统调用,强制终止进程。

2. 线程终止与用户态资源清理:

– 主线程终止:正常退出时,main函数执行完毕后调用exit函数,exit会执行用户编写的退出逻辑(如保存配置、关闭文件流),再调用_exit系统调用;异常退出时,直接终止主线程,不执行用户退出逻辑;

– 子线程清理:内核遍历进程所有子线程,若子线程处于可终止状态,发送SIGTERM信号通知终止,等待子线程执行收尾逻辑(正常退出)或强制终止(异常退出),避免子线程残留;

– 用户态资源释放:释放程序动态申请的资源,如堆内存(free、delete)、文件描述符(close)、网络连接(close)、GDI资源、COM组件(Linux下为共享库资源)等;glibc会自动清理自身分配的资源(如glibc堆),异常退出时无法完成该操作,需内核兜底。

3. 共享库卸载与依赖清理:

动态链接器按共享库加载顺序的逆序,卸载所有依赖的共享库,卸载过程中调用共享库的析构函数(如destructor属性修饰的函数),执行共享库自身的清理逻辑;同时递减共享库引用计数,引用计数为0时,通过munmap系统调用释放共享库占用的虚拟内存。

4. 进程终止与内核态资源回收:

– 进程状态切换:内核调用exit_group系统调用,将进程所有线程状态切换为“终止”(EXIT_ZOMBIE),标记进程为可回收;

– 内核资源回收:销毁进程控制块(task_struct),回收进程ID(PID)、虚拟地址空间(mm_struct)、文件描述符表、信号掩码等内核资源;释放进程占用的物理内存、页表等资源,确保无内核级资源泄漏;

– 调试器通知(若有):若程序被gdb等调试器附加,内核会通知调试器进程已终止,调试器可获取进程退出状态,用于调试分析。

5. 退出状态反馈:

进程终止后,会返回一个退出码(0表示正常退出,非0表示异常退出,不同非0值对应不同异常原因);父进程可通过wait、waitpid系统调用获取子进程退出码,判断子进程是否正常退出,进而执行后续逻辑;若父进程未及时获取退出码,子进程会变为僵尸进程(Zombie),直至父进程获取退出码或父进程终止,僵尸进程由init进程(PID=1)回收。

核心要点:正常退出与异常退出的核心区别是“是否执行用户态清理逻辑”,正常退出会完整执行收尾代码,异常退出则直接强制终止,依赖内核兜底回收资源;Linux下僵尸进程是退出流程的常见场景,需通过wait/waitpid避免其残留。

总结:启动-退出完整流程核心链路

用户触发启动指令(execve系统调用)→ ELF文件定位与路径解析 → 内核态ELF合法性与安全校验 → 进程创建(task_struct初始化)与资源分配 → 动态链接器加载与共享库解析 → ELF文件映射、重定位与符号解析 → 主线程调度与入口执行(_start→main) → 程序运行与系统监控 → 触发退出指令(exit/_exit/exit_group) → 线程清理与用户态资源释放 → 共享库卸载 → 进程终止与内核资源回收 → 退出码反馈。

整个流程覆盖Linux平台ELF格式、动态链接、进程调度、信号机制等核心底层技术,补充了静态/动态链接差异、僵尸进程、核心转储、信号处理等易遗漏要点;理解这一完整闭环,有助于排查程序启动失败(如共享库缺失、权限不足、ELF损坏)和退出异常(如资源泄漏、僵尸进程、崩溃退出)等问题,也能为程序优化(如启动速度、资源占用、退出稳定性)提供方向。

程序是如何启动的(Windows平台)

程序是如何启动的

程序是如何启动的(Windows平台)

Windows平台下的可执行程序以PE(Portable Executable)格式存储于磁盘,启动的核心本质是将PE文件从磁盘加载至内存,完成进程初始化与指令执行,最终实现程序运行;而程序退出则是反向流程,核心是终止指令执行、回收系统资源,确保无资源泄漏。整个流程涉及系统调用、内存管理、进程调度等核心机制。本文将按步骤拆解Windows平台下可执行程序(.exe)的启动及退出流程。

步骤1:触发启动指令(用户态触发与系统调用)

程序启动的触发源于用户操作,本质是触发系统调用,向操作系统发起进程创建请求,常见触发方式及底层逻辑如下:

– 双击桌面图标/开始菜单启动:图标本质是快捷方式(.lnk文件),系统解析快捷方式指向的PE文件路径,最终调用CreateProcess函数发起进程创建请求;

– 右键“打开”或命令行启动:直接指定PE文件路径,通过ShellExecute或CreateProcess函数触发启动流程,命令行启动可通过cmd或PowerShell传入启动参数;

– 其他触发方式:通过进程间通信(IPC)、服务启动(services.msc)等方式,本质也是通过系统调用触发PE文件加载;此外,通过调试器(如Visual Studio)启动程序,会额外触发调试器附加逻辑,同步监控进程启动全过程。

核心要点:所有启动方式最终都会映射到Windows API的进程创建接口(CreateProcess最终调用ntdll.dll的NtCreateProcessEx),由用户态切换至内核态,启动内核态的进程创建流程;若启动时携带管理员权限请求,会触发UAC弹窗校验,通过后以高权限启动进程。

步骤2:PE文件定位与路径解析

系统接收到启动请求后,首要任务是定位目标PE文件,完成路径解析与合法性校验前置:

1. 路径解析:系统根据触发指令中的路径(快捷方式指向路径、命令行输入路径),通过文件系统驱动(NTFS/FAT32)定位磁盘上的PE文件,获取文件句柄;若路径为相对路径,系统会按环境变量(PATH)顺序查找PE文件;

2. 初步校验:确认文件存在且为可执行类型(文件头标识为0x4D5A,即“MZ”标识),排除非PE格式文件,避免无效启动请求。

核心要点:路径解析过程依赖Windows文件系统驱动(如ntfs.sys),涉及文件句柄的创建与权限校验(如当前用户是否有读取该PE文件的权限),为后续文件读取与加载奠定基础;若路径解析失败(如文件不存在、权限不足),会直接返回“找不到指定文件”“权限不足”等错误。

步骤3:PE文件合法性校验(内核态安全校验)

定位PE文件后,系统会在 kernel32.dll 与 ntdll.dll 的协同下,完成PE文件的合法性与安全性校验,避免恶意文件或损坏文件启动,核心校验内容如下:

1. PE文件完整性校验:解析PE文件头(IMAGE_DOS_HEADER、IMAGE_NT_HEADERS),校验文件结构是否完整,是否存在文件截断、篡改等问题;

2. 数字签名校验:校验PE文件的数字签名(若存在),确认文件未被篡改、来源合法,由Windows验证服务(WinVerifyTrust)完成;

3. 安全策略校验:结合系统安全策略(如UAC权限、杀毒软件实时监控),检测文件是否包含恶意代码、是否符合系统安全规则;

校验失败则终止启动流程,弹出对应错误提示(如“文件损坏”“数字签名无效”“权限不足”);校验通过则进入后续加载流程;补充说明:部分第三方杀毒软件会拦截校验过程,对可疑PE文件进行额外扫描,扫描不通过也会终止启动。

步骤4:进程创建与系统资源分配

合法性校验通过后,系统会创建新的进程(Process)与线程(Thread),并为其分配必要的系统资源,核心操作如下:

1. 进程创建:内核态调用NtCreateProcess函数,创建进程控制块(PCB,即EPROCESS结构体),分配进程ID(PID),设置进程优先级、权限掩码等核心属性,进程初始状态为“就绪”;

2. 线程创建:调用NtCreateThread函数,创建主线程(初始线程),分配线程ID(TID),将主线程与进程关联,主线程初始状态为“就绪”,等待CPU调度;

3. 资源分配:

– 内存分配:通过虚拟内存管理机制,为进程分配虚拟地址空间,划分代码段(.text)、数据段(.data/.bss)、堆、栈等区域,将PE文件从磁盘映射至虚拟内存(采用内存映射文件机制,提升读取效率);

– 其他资源:分配文件句柄、注册表访问权限、网络权限等,确保程序运行所需的资源可用。

核心要点:进程是资源分配的基本单位,线程是调度执行的基本单位,虚拟内存映射是PE文件加载的核心机制(通过CreateFileMapping和MapViewOfFile实现),避免将整个文件一次性加载至物理内存,节省资源;此外,系统会为进程分配默认的堆空间(由ntdll.dll初始化),供程序运行时动态申请内存。补充缺失点:进程创建时会继承父进程的环境变量(如PATH、USERPROFILE),环境变量会用于后续DLL查找、文件路径解析等操作;同时会初始化进程的句柄表,用于管理进程所有打开的内核对象(文件句柄、线程句柄等)。

步骤5:PE文件加载与依赖解析(DLL加载)

进程与资源分配完成后,系统会完成PE文件的加载与依赖动态链接库(DLL)的解析,核心流程如下:

1. PE文件加载:根据PE文件头中的节表信息,将代码段、数据段等内容从磁盘加载至虚拟内存的对应地址,完成重定位(解决代码中绝对地址的偏移问题,确保指令能正确执行);

2. DLL依赖解析:遍历PE文件的导入表(IMAGE_IMPORT_DESCRIPTOR),解析程序依赖的所有DLL文件(如kernel32.dll、user32.dll等系统核心DLL),按顺序加载所有依赖DLL;

3. 导入表填充:DLL加载完成后,将DLL中导出函数的地址填充至程序的导入表中,确保程序能正常调用DLL中的函数;若缺少依赖DLL或DLL版本不兼容,会弹出“缺少XXX.dll”错误,终止启动。

核心要点:DLL加载采用“延迟加载”机制(可通过编译选项配置,对应/DELAYLOAD链接器选项),非必要DLL会在程序调用时才加载,提升启动效率;重定位是PE文件加载的关键(通过重定位表IMAGE_BASE_RELOCATION实现),确保程序在不同虚拟地址空间中能正常执行;补充:若PE文件启用了ASLR(地址空间布局随机化),虚拟内存加载地址会随机分配,进一步提升安全性。补充缺失点:DLL加载时会检查DLL的依赖(即DLL的导入表),若DLL存在依赖链(如A.dll依赖B.dll),会递归加载所有依赖DLL;此外,系统会维护DLL的引用计数,每加载一次引用计数加1,卸载一次减1,引用计数为0时才会彻底释放DLL内存。

步骤6:主线程启动与程序入口执行

PE文件与依赖DLL加载完成后,系统会调度主线程启动,执行程序入口指令,完成程序初始化,核心流程如下:

1. 主线程调度:CPU调度器根据进程优先级,将主线程从“就绪”状态切换为“运行”状态,开始执行指令;

2. 入口点执行:主线程首先执行PE文件头中指定的入口点(Entry Point),对于C/C++编写的程序,入口点通常是mainCRTStartup(控制台程序)或WinMainCRTStartup(窗口程序),而非用户编写的main/WinMain函数;

3. 程序初始化:入口函数会完成CRT(C运行时库)初始化、全局变量/静态变量初始化、线程局部存储(TLS)初始化、窗口创建(窗口程序,调用CreateWindowEx)、资源初始化等操作,最终执行用户编写的核心逻辑(main/WinMain函数),程序界面(若有)显示,启动完成;补充:若程序是控制台程序,会自动创建控制台窗口,关联标准输入/输出流。补充缺失点:入口函数执行前,系统会完成PE文件的IAT(导入地址表)修复,将导入表中DLL函数的“占位地址”替换为实际的函数地址,确保程序能正常调用DLL函数;对于带manifest清单的程序,会加载清单中指定的依赖组件(如公共控件库),确保程序界面兼容性。

步骤7:进程运行与系统监控

程序启动完成后,进入运行状态,系统会通过内核态进程监控机制,全程管理进程的运行,核心监控与管理操作如下:

– 进程调度:CPU调度器根据进程优先级、线程状态,动态调度进程的线程执行,实现多进程、多线程并发运行;

– 异常处理:若程序出现异常(如内存访问越界、断言失败),系统会触发异常处理机制(SEH,结构化异常处理),若程序未注册自定义异常处理函数,系统会弹出“程序无响应”或“程序崩溃”提示,可选择调试或强制关闭;

– 资源管理:实时监控进程的资源占用(内存、CPU、磁盘I/O),若资源占用过高,系统会进行资源调度;进程终止时,回收其占用的所有系统资源(虚拟内存、文件句柄等),避免资源泄漏。补充缺失点:运行过程中,进程可通过系统调用(如VirtualAlloc、VirtualFree)动态申请/释放虚拟内存,系统会根据物理内存使用情况,进行页面置换(页面调入/调出),确保进程正常运行;同时,系统会监控进程的句柄泄漏问题,若进程打开句柄后未及时关闭,会记录句柄信息,便于排查问题。

步骤8:程序退出流程(核心操作与资源回收)

程序退出是启动流程的反向操作,核心目标是安全终止指令执行、彻底回收所有分配的系统资源,避免资源泄漏,分为“正常退出”和“异常退出”两种场景,底层操作统一且严谨,具体步骤如下:

1. 触发退出指令(两种场景):

– 正常退出:由用户主动操作(如点击窗口关闭按钮、快捷键Ctrl+F4)或程序自身逻辑触发(如执行完main/WinMain函数后返回),最终调用ExitProcess函数(用户态),发起退出请求;

– 异常退出:程序运行中出现未处理异常(如内存崩溃、断言失败)、被系统强制终止(如任务管理器结束进程)或调试器终止,由系统调用TerminateProcess函数(内核态),强制触发退出流程。

2. 线程终止与资源清理(用户态):

– 主线程终止:若为正常退出,主线程会先执行用户编写的退出逻辑(如保存配置文件、关闭文件流),再执行CRT终止函数(如exit、_exit),完成全局变量、静态变量的销毁,释放线程局部存储(TLS)资源;

– 子线程清理:系统会遍历当前进程的所有子线程,若子线程处于可终止状态,调用TerminateThread函数强制终止(异常退出)或等待子线程执行完收尾逻辑后终止(正常退出),避免子线程残留导致资源泄漏;

– 用户态资源释放:释放程序运行中动态申请的资源,如堆内存(free、delete)、文件句柄(CloseHandle)、网络连接(closesocket)、注册表句柄等,若程序未主动释放,后续会由系统兜底回收,但可能存在延迟。补充缺失点:用户态资源还包括GDI资源(如画笔、画刷、窗口句柄)、COM组件(需调用Release释放),这类资源若未主动释放,容易导致资源泄漏,甚至影响系统稳定性;正常退出时,CRT会自动清理自身分配的资源(如CRT堆),异常退出时则无法完成。

3. DLL卸载与依赖清理:

系统会反向遍历程序的导入表,按加载顺序的逆序卸载所有依赖的DLL文件,卸载过程中会调用DLL的DllMain函数(传入DLL_PROCESS_DETACH参数),执行DLL自身的清理逻辑(如释放DLL分配的内存、关闭DLL打开的资源);若DLL被多个进程共享,则仅减少引用计数,直至所有进程卸载后,才彻底释放DLL占用的内存。

4. 进程终止与内核态资源回收:

– 进程状态切换:系统调用NtTerminateProcess函数(内核态),将进程状态从“运行”或“就绪”切换为“终止”状态,标记进程为可回收;

– 内核资源回收:销毁进程控制块(EPROCESS结构体),回收进程ID(PID)、虚拟地址空间(释放所有虚拟内存映射,包括PE文件映射、堆、栈),回收进程占用的内核资源(如文件句柄、网络端口、注册表权限等);

– 调试器通知(若有):若程序被调试器附加,系统会通知调试器进程已终止,调试器可执行后续调试逻辑(如记录退出状态、分析崩溃原因)。

5. 退出状态反馈:

进程终止后,会返回一个退出码(Exit Code),用于标识退出状态(0表示正常退出,非0表示异常退出,不同非0值对应不同异常原因,如1表示参数错误、2表示文件缺失);父进程可通过WaitForSingleObject等函数获取子进程的退出码,判断子进程是否正常退出,进而执行后续逻辑。

核心要点:正常退出与异常退出的核心区别的是“是否执行用户态清理逻辑”——正常退出会完整执行程序自身的收尾代码,异常退出则直接强制终止,可能导致部分用户态资源未主动释放,需依赖系统兜底回收;无论哪种退出方式,系统都会确保内核态资源彻底回收,避免系统级资源泄漏。补充缺失点:异常退出时,系统会生成崩溃转储文件(.dmp),用于后续调试分析崩溃原因;若程序注册了异常回调函数(如SetUnhandledExceptionFilter),异常退出前会执行回调函数,可用于记录日志、保存关键数据;此外,进程退出时会发送WM_QUIT消息(窗口程序),通知所有窗口进行清理,确保窗口资源正常释放。

总结:启动-退出完整流程核心链路

用户触发启动指令(CreateProcess调用)→ PE文件定位与路径解析 → 内核态合法性与安全校验 → 进程/线程创建与资源分配 → PE文件加载与DLL依赖解析 → 主线程调度与入口点执行 → 程序初始化与运行 → 系统全程监控 → 触发退出指令(ExitProcess/TerminateProcess)→ 线程终止与用户态资源清理 → DLL卸载 → 进程终止与内核态资源回收。

整个流程涉及用户态与内核态的切换、虚拟内存管理、进程调度、DLL机制等核心Windows底层技术,补充遗漏要点:启动过程中还涉及PE文件的基址重定位、ASLR安全机制、CRT初始化、IAT修复、环境变量继承等关键环节;退出过程则重点实现资源彻底回收、崩溃转储生成、窗口消息通知与状态反馈;额外补充:启动与退出流程中,系统会通过ntdll.dll中的系统调用(如NtCreateProcessEx、NtTerminateProcess)完成用户态与内核态的切换,切换过程会涉及上下文保存与恢复,确保指令执行的连续性。理解这一完整闭环,有助于排查程序启动失败(如DLL缺失、权限不足、文件损坏、基址冲突、IAT修复失败)和退出异常(如资源泄漏、崩溃退出、句柄泄漏)等问题,也能为程序优化(如启动速度、资源占用、退出稳定性)提供方向。

通过ADB删除小米电视安装应用

小米电视越来越慢,就想多删除一些应用:
注意:预装应用删除后,除了升级系统,并没有好的恢复方法,使用命令需谨慎

#首先在小米电视上打开远程调试,让ADB可以远程连接
开启开发者模式:设置-》关于-》产品型号-》遥控连续按5下OK键
设备安全设置:设置-》账号与安全-》允许安装未知来源的应用-》允许ADB调试
查看网络IP:设置-》网络-》找到电视使用的局域网IP

#远程连接小米电视
adb connect IP

#显示已连接设备
adb devices

#安装APK包
adb install -r PATH_TO_APK/MyAPK.apk

#列出已安装的包
adb shell pm list packages

#删除具体应用(不同版本包名有差异)
应用商店
adb shell pm uninstall com.xiaomi.mitv.appstore
小米商城
adb shell pm uninstall com.xiaomi.mitv.shop
小米支付
adb shell pm uninstall com.xiaomi.mitv.payment
小米钱包
adb shell pm uninstall com.mipay.wallet.tv
天气
adb shell pm uninstall com.xiaomi.tweather
时尚画报
adb shell pm uninstall com.xiaomi.tv.gallery
相册
adb shell pm uninstall  com.mitv.gallery
照片屏幕保护程序
adb shell pm uninstall  com.android.dreams.phototable
游戏中心
adb shell pm uninstall com.xiaomi.mibox.gamecenter
日历
adb shell pm uninstall com.xiaomi.mitv.calendar
提醒
adb shell pm uninstall com.mitv.alarmcenter
用户手册
adb shell pm uninstall com.xiaomi.mitv.handbook
热点新闻
adb shell pm uninstall com.duokan.videodaily
新闻
adb shell pm uninstall  com.xiaomi.mitvnews
米家
adb shell pm uninstall com.xiaomi.smarthome.tv
小米盒子设置
adb shell pm uninstall com.xiaomi.mitv.settings

#重启设备
adb shell reboot
adb shell reboot -p

jekyll环境搭建

大家都知道github pages是用了jekyll,我们自己如何搭建一个网站呢?

#安装依赖环境
apt-get install ruby ruby-dev

#安装jekyll
gem install bundler jekyll

#新建网站
jekyll new mysite

#开启网站
cd mysite
bundle exec jekyll serve --host 0.0.0.0

#浏览网站
http://172.16.172.80:4000

语法插件从WP-Syntax替换为SyntaxHighlighter Evolved

今天正好有些时间,索性将语法插件从WP-Syntax替换为SyntaxHighlighter Evolved。

由于是通过后台SQL进行处理的,出现了不少问题,经过3小时奋战,还是调整的差不多了。

现在有两个问题:
1、太长的代码要么不换行,要么换行不好看
2、有些语法不支持,如per,Io,go等
3、应该还有一些SQL操作的遗留问题,后面发现再调整吧

	'as3'           => 'as3',
	'actionscript3' => 'as3',
	'bash'          => 'bash',
	'shell'         => 'bash',
	'coldfusion'    => 'coldfusion',
	'cf'            => 'coldfusion',
	'clojure'       => 'clojure',
	'clj'           => 'clojure',
	'cpp'           => 'cpp',
	'c'             => 'cpp',
	'c-sharp'       => 'csharp',
	'csharp'        => 'csharp',
	'css'           => 'css',
	'delphi'        => 'delphi',
	'pas'           => 'delphi',
	'pascal'        => 'delphi',
	'diff'          => 'diff',
	'patch'         => 'diff',
	'erl'           => 'erlang',
	'erlang'        => 'erlang',
	'fsharp'        => 'fsharp',
	'groovy'        => 'groovy',
	'java'          => 'java',
	'jfx'           => 'javafx',
	'javafx'        => 'javafx',
	'js'            => 'jscript',
	'jscript'       => 'jscript',
	'javascript'    => 'jscript',
	'latex'         => 'latex', // Not used as a shortcode
	'tex'           => 'latex',
	'matlab'        => 'matlabkey',
	'objc'          => 'objc',
	'obj-c'         => 'objc',
	'perl'          => 'perl',
	'pl'            => 'perl',
	'php'           => 'php',
	'plain'         => 'plain',
	'text'          => 'plain',
	'ps'            => 'powershell',
	'powershell'    => 'powershell',
	'py'            => 'python',
	'python'        => 'python',
	'r'             => 'r', // Not used as a shortcode
	'splus'         => 'r',
	'rails'         => 'ruby',
	'rb'            => 'ruby',
	'ror'           => 'ruby',
	'ruby'          => 'ruby',
	'scala'         => 'scala',
	'sql'           => 'sql',
	'vb'            => 'vb',
	'vbnet'         => 'vb',
	'xml'           => 'xml',
	'xhtml'         => 'xml',
	'xslt'          => 'xml',
	'html'          => 'xml',

Fix Android “Too Many Pattern Attempts”

前几天外出时,Android手机放口袋里,不知道怎么碰到了,超过了解锁图案的最大重试次数,屏幕上显示“Too Many Pattern Attempts”,还要我登录Google账号。

Google账号我能登录,但问题是,Google最近被封掉了,根本连不上啊。

经过查找,发现有两种方法可以解决这个问题,一种是直接重置手机,后果是资料全没;一种是用ADB方法,跳过验证。

手机资料全清空,当然是不可以接受的!于是就使用了ADB方法。

前提是:
1、手机root过
2、ADB调试功能打开

连线,执行下面的命令:

adb root
adb shell
rm /data/system/gesture.key

这样,重启后,就可以跳过图形验证,直接进入设置界面了。

然后,开SSH,登录Google账号,重置图形验证,搞定!

参考:
Too Many Pattern Attempts

修改WordPress域名

比如,我需要把域名dm01.neohope.org换为dm02.neohope.org,需要进行以下操作:

1、如果需要,可以重命名wordpress文件夹

2、如果需要,修改wordpress的数据库设置

3、更新wordpress主配置

UPDATE wp_options SET option_value = replace( option_value, 'dm01.neohope.org', 'dm02.neohope.org' ) WHERE option_name = 'home' OR option_name = 'siteurl';  

4、更新posts

UPDATE wp_posts SET post_content = replace( post_content, 'dm01.neohope.org', 'dm02.neohope.org' ) ;  
UPDATE wp_posts SET guid = replace( guid, 'dm01.neohope.org', 'dm02.neohope.org' ) ;  

5、更新comments

UPDATE wp_comments SET comment_content = replace(comment_content, 'dm01.neohope.org', 'dm02.neohope.org') ;
UPDATE wp_comments SET comment_author_url = replace(comment_author_url, 'dm01.neohope.org', 'dm02.neohope.org')

6、搞定

Windows下Emacs使用SBCL

今天配置Emacs时,发现Emacs无法使用SBCL,其默认为lisp。
会报下面的错误:

Searching for program: no such file or directory, lisp 

这个问题可以用一个变通的方法解决,在Emacs路径下建立下面的文件:
lisp.bat

@rem start sbcl
D:\CommonLisp\sbcl-1.1.17\sbcl.exe --core "D:\CommonLisp\sbcl-1.1.17\sbcl.core"

然后就可以使用啦。

还是linux下面简单一些,符号链接直接搞定:

ln -s /usr/bin/clisp /usr/bin/lisp