Linux
未读基础 IO —— C 语言文件 I/O 操作基础 前言1. 文件的基本概念 文件的定义: 文件 = 内容(数据) + 属性(如权限、修改时间、所有者等)。 属性是文件管理的重要依据,贯穿文件的存储、访问和控制全流程。 2. 文件的两种状态 打开的文件 触发条件:由进程主动打开(如读写操作)。 存储位置:加载到内存中。 核心机制: 进程关联:每个打开的文件需与进程绑定,形成“进程-文件”的 多对一关系(1: n 关系:一个进程可打开多个文件)。 内核管理:操作系统为每个打开的文件创建 文件打开对象(如 struct XXX),记录文件属性、状态及链表指针(如 next),便于统一管理。 未打开的文件 存储位置:磁盘上。 核心问题:如何高效组织海量未打开文件,支持 快速增删查改。 管理目标:通过目录结构、文件系统层级等实现文件分类与定位。 3. 操作系统如何管理打开的文件 管理原则 —— 先描述,后组织: 描述:为每个打开的文件创建内核对象(如 struct file),记录文件属性(如读写位置、权限)和操作接口。 组织:通过链表、哈希 ...
Linux
未读fork + exec 系列函数创建子进程并执行其他程序(跨语言衔接示例) CSDN 相关文章 fork(2) - Linux 手册页 exec(3) - Linux 手册页 在 Linux 系统编程中,fork() 和 exec() 是进程控制的两大核心。本文将演示如何使用 fork 创建子进程,再使用 exec 执行外部程序,展示多语言之间的 协同执行 过程。 1. fork + exec 基础原理 fork():复制当前进程,返回两次(一次给父进程,一次给子进程) exec*():用新程序 替换当前进程映像(进程 ID 不变) 通常模式: 12345678910pid_t pid = fork();if (pid == 0) // 子进程执行新程序{ execl("/bin/ls", "ls", "-l", NULL); perror("execl"); // 出错处理 ...
Linux
未读从零到一实现一个简易 Shell这应该是个蛮有趣的话题:“什么是 Shell”?相信只要摸过计算机,对于操作系统(不论是 Linux、Unix 或者是 Windows)有点概念的朋友们大多听过这个名词,因为只要有“操作系统“那么就离不开 Shell 这个东西。不过,在讨论 Shell 之前,我们先来了解一下计算机的运行状况吧!举个例子来说:当你要计算机传输出来“音乐”的时候,你的计算机需要什么东西呢? 硬件:当然就是需要你的硬件有“声卡芯片”这个配备,否则怎么会有声音; 核心管理:操作系统的核心可以支持这个芯片组,当然还需要提供芯片的驱动程序啰; 应用程序:需要使用者(就是你)输入发生声音的指令啰! 这就是基本的一个输出声音所需要的步骤!也就是说,你必须要“输入”一个指令之后,“硬件“才会通过你下达的指令来工作!那么硬件如何知道你下达的指令呢?那就是 kernel(核心)的控制工作了!也就是说,我们必须要通过“Shell”将我们输入的指令与 Kernel 沟通,好让 Kernel 可以控制硬件来正确无误的工作!基本上,我们可以通过下面这张图来说明一下: 以上内容摘自《鸟哥的 L ...
Linux
未读进程控制 —— 进程程序替换1. 替换原理进程程序替换是指在一个已经存在的进程中,通过系统调用将当前进程的代码、数据等全部替换为新程序的内容,也就是说,新程序加载到当前进程的地址空间中,原来进程的内容被完全覆盖。在这一过程中,进程的 PID 保持不变,但内存空间、寄存器的内容和代码逻辑均变为新程序的内容。 【关键点】 进程替换不创建新进程,而是复用现有进程的 PID,这对需要快速切换任务、降低资源开销有很大好处。 一旦替换成功,原进程的代码 立即终止,从新程序的 main 函数开始执行。 替换过程会关闭原进程中打开的文件描述符(除非这些描述符被标记为在 exec 之后保留),fcntl(fd, F_SETFD, FD_CLOEXEC) 标记文件描述符在 exec 后关闭(了解)。 2. execl 的单进程使用1. 函数原型12345678910int execl(const char* path, const char* arg, ..., (char*)NULL);注意:execl 接受变长参数,需要以 NULL 结束参数列表,参数顺序要求严格,第一个参数通常写为程序文件的 ...
Linux
未读进程控制 —— 进程等待1. 进程等待必要性 当父进程通过 fork() 创建了子进程后,子进程终止时,其退出信息必须由父进程读取,父进程如果不管不顾,就可能造成 僵尸进程 的问题,进而造成内存泄漏。 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的 kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。 最后,父进程派给子进程的任务完成的如何,我们需要知道。如子进程运行完成,结果对还是不对,或者是否正常退出。 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。 如果不等待会怎样? 子进程退出了,但父进程没有调用 wait() 系列函数。 子进程的“退出状态”会保留在内核中,直到父进程读取它。 此时子进程的 PCB 没有完全释放,占用系统资源。 如果产生大量僵尸进程,系统资源将耗尽,导致无法创建新进程。 所以:父进程需要“等待”子进程终止并获取其退出状态,以释放系统资源。 面试点拨: 如果不调用 wait() 会怎样? 回答:子进程的退出信息留在内核,PCB 未释放,形成僵尸进程,长期不回收会占满系统资源。 2. 常用等待方法 ...
Linux
未读进程控制 —— 终止进程一、进程退出场景从我们的视角来看进程终止的场景一般就是以下三种: 代码运行完毕,结果正确(一般不关心)。 代码运行完毕,结果不正确。 代码异常终止。 但是进程也可能因多种原因终止,比如: 场景 说明 正常完成任务 程序执行完所有代码逻辑后退出 异常错误终止 遇到不可恢复的错误(如段错误、除零错误) 主动终止 调用退出函数(exit()/_exit())或通过 return 退出 被动终止 收到终止信号(如 SIGKILL、SIGTERM) 被父进程杀死 父进程调用 kill() 函数发送信号,使子进程退出。 看进程终止的角度、进程终止的原因等不同方面来解释进程的终止,虽然说法上不同,但也大同小异,我们只需要记住一点: 所有进程的退出方式都可以归为两大类:正常退出 和 异常退出,而主动或被动,是从行为发起方角度来分的。进程出现异常,本质是我们的进程收到了对应的信号!! 二、进程的退出码 我们都知道 main 函数是代码的入口,但实际上 main 函数只是用户级别代码的入口,main 函数也是被其他函数调用的, ...
使用 Git 从 HTTPS 切换至 SSH起因是我在 Linux 中使用 git push 推送经常卡住或失败,真的很烦,于是选择 切换成 SSH ,可以提高稳定性和体验,比较适合在 Linux 系统开发环境中长期使用。当然本文的方法也同样适用于 Windows。 1. 确认是否已生成 SSH 密钥对先检查是否已有 .ssh/id_rsa 文件: 1ls ~/.ssh 如果看到了 id_rsa 和 id_rsa.pub 就说明已经有了密钥对。如果没有,就执行下面的命令生成: 1ssh-keygen -t rsa -b 4096 -C "你的邮箱" 一路回车即可生成(个人使用,建议不设密码短语,会方便些)。 2. 将 SSH 公钥添加到远程仓库平台(如 GitHub / Gitee)1. 复制公钥内容1cat ~/.ssh/id_rsa.pub 复制输出的整段文本(以 ssh-rsa 开头,以邮箱结尾)。对应 Windows 实际路径为 C:\Users\你的用户名\.ssh\。 2. 添加到远程平台1. GitHub: 登录 GitHub。 点 ...
Linux
未读进程控制 —— 进程创建一、fork() 函数基础1. fork() 的作用 创建子进程:通过复制父进程的地址空间生成一个新进程。 调用一次,返回两次: 父进程返回子进程的 PID(即 > 0 or 正数)。 子进程返回 0。 失败返回 -1。 1pid_t fork(void); 2. 写时拷贝(Copy-On-Write, COW) 机制:fork 时不会立刻复制父进程的所有内存页。fork() 后,父子进程 共享物理内存(共享内存页(只读)),直到一方尝试修改数据时,内核才复制该内存页。 修改时触发“页错误” 操作系统才会为该进程分配新的物理页,完成“真正拷贝” 优点: 提升效率: 减少 fork() 的开销(避免立即复制全部内存)。 节省内存开销: 节省物理内存(共享未修改的页)。 [!NOTE] 通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图: 二、代码示例示例 1:基础 fork() 使用123456789101112#include <stdio.h ...
Linux
未读程序地址空间入门程序的 地址空间 是操作系统为每个程序分配的内存区域,它决定了程序如何访问存储在计算机内存中的数据。程序地址空间包括了多个部分,每一部分有不同的用途。通过合理管理地址空间,操作系统可以有效地进行内存分配和保护。 1. 地址空间的划分程序的地址空间通常分为多个段,每个段具有不同的功能。这些段的划分通常是由操作系统定义的。下图是基于 kernel 2.6.32 和 32位平台 的典型空间布局图: 然而,一个标准的程序地址空间布局图(32 位系统)包括: 更详细点来说则是: 1234567891011121314151617181920212223242526+------------------------+ <-- 0xC0000000(内核空间起始,用户不可访问) | 内核空间 | +------------------------+ <-- 0xBFFFFFFF(用户空间结束) | 栈(Stack) | ↓ 向低地址增长 | - 主线程栈 | +------ ...
Linux 2.6内核进程调度队列(了解) 更多文章 | CSDN 更多文章 | 知乎 更多文章 | 腾讯云 Linux 2.6 内核在进程调度方面引入了 O(1) 调度器,该调度器的核心目标是保证调度决策的时间复杂度为恒定时间 O(1),即无论系统中有多少个进程,调度器的决策时间始终保持不变。这个设计对于大规模系统具有重要意义,可以有效避免调度决策的性能瓶颈。 1. 调度队列结构Linux 2.6 内核的进程调度通过多个调度队列进行管理,核心数据结构是 运行队列(Runqueue),每个 CPU 核心都维护一个独立的运行队列。运行队列(Runqueue):每个 CPU 核心维护一个独立的运行队列,队列内包含当前可运行的进程。运行队列结构主要由以下几个组件组成: 优先级数组: 活跃数组(Active Array):存放当前可运行的进程,按优先级分为 140 个队列(0~139)。 0~99:实时进程(RT进程,如硬实时任务)。 100~139:普通进程(分时进程,如用户程序)。 过期数组(Expired Array):存放已经用完时间片的进程,结构与活跃数组相同。 时间片管 ...











