
Linux基础命令查看进程信息ps -aux | grep a.out a所有用户进程 u显示所有者、CPU、内存 x无终端进程也显示查看线程信息ps -elf | grep a.out -e 列出所有进程 -l 长格式显示更多详细字段 -f 完整格式显示命令行、PPID父进程号什么是进程:程序的一次运行实例称为进程程序被加载进内存、操作系统为它分配资源后正在运行的实体进程是操作系统资源分配的最小单位。资源包括内存空间、文件描述符、信号处理表、工作目录、用户权限等。在系统中使用什么结构体表示一个进程Linux 内核中每一个进程在内核里都对应一个struct task_struct结构体这个结构体就是大名鼎鼎的PCB Process Control Block 进程控制块。所有进程的task_struct被内核放进一个双向循环链表叫进程链表内核遍历这条链表就能查到系统所有进程就是ps命令的数据源。系统通过什么来区分两个正在运行的进程Process ID进程号唯一区分每一个运行中的 task_struct进程。PPID父进程 ID只是记录它的父进程是谁多个子进程可以有同一个 PPID不唯一不能区分进程。当一个进程运行后32bit的系统给用户分配的内存空间多大有哪些区域每个区域存储什么32 位地址总线最大寻址空间2的32次方4GB用户空间0 ~ 3GB0x00000000 ~ 0xBFFFFFFF 大小 3GB内核空间3GB ~ 4GB0xC0000000 ~ 0xFFFFFFFF 大小 1GB用户空间 3GB 内部分区从低地址→高地址地址从小到大顺序代码段 → 数据段 → BSS 段 → 堆 → 栈 → 共享库区1. 代码段 .text存放编译好的机器指令、程序二进制代码属性为只读可执行防止运行时修改指令声明周期为整个进程运行期间2. 初始化数据段 .data数据段存放已经初始化的全局变量、static 静态变量属性可读、可写入3. 未初始化数据段 .bss存放未初始化的全局变量、未初始化静态变量程序加载时内核会自动把这片内存清零。.text .data .bss 这三块的大小在编译时就已经确定运行中不会扩张收缩。4. 堆 heap属性读写运行时可以动态扩大、缩小。手动释放 free ()忘记释放 → 内存泄漏。堆区位于 BSS 上方地址向上增长从低地址往高地址扩张。申请堆区空间的函数malloc() calloc() realloc()5. 共享库 / 内存映射区 mmap堆和栈中间的一片区域。存放动态库 .so 文件shturl.、mmap 映射的文件、匿名共享内存、线程栈也常分配在这里6. 用户栈 stack栈位置靠近 3GB 边界的高地址向下增长从高地址→低地址默认大小一般 8MB。存放局部变量、函数参数、函数返回地址、ebp栈帧、寄存器现场、函数调用的时候栈自动伸缩函数返回自动释放。递归太深栈不断向下扩张超出 8MB →栈溢出 Segmentation fault0x00000000 ┌─────────────────────┐ │ .text 代码段 │ ├─────────────────────┤ │ .data 初始化数据 │ ├─────────────────────┤ │ .bss 未初始化数据 │ ├─────────────────────┤ │ heap 堆 ↑向上扩 │ │ │ │ 空闲间隙 │ │ mmap共享库区 │ │ │ │ stack 栈 ↓向下扩 │ ├─────────────────────┤ │ 命令行参数、环境变量 │ 0xBFFFFFFF (3G边界) | ─────────────────────── 0xC0000000 ┌─────────────────────┐ │ 内核空间 1GB │ │ (所有进程共享) │ └─────────────────────┘ 0xFFFFFFFF (4G)每个进程 3GB 用户空间开 10 个进程是不是就要 30GB 物理内存不是虚拟内存≠物理内存内核通过页表把虚拟地址映射到物理内存多个进程虚拟地址可以映射到不同物理页也可以共享物理页共享库。什么是虚拟地址虚拟地址是操作系统为每个应用进程独立分配的虚构内存地址并非内存条上真实的物理地址。所有程序代码、指针操作、变量地址全程使用的都是虚拟地址CPU 不会直接通过虚拟地址读写真实内存。硬件 MMU 系统页表会自动完成虚拟地址→物理地址的映射转换 进程只认虚拟地址无需感知真实内存位置无映射的虚拟地址访问会触发缺页异常由系统动态分配内存。核心作用内存隔离安全每个进程虚拟地址空间独立相同虚拟地址互不干扰杜绝进程互相篡改内存、程序崩溃连锁影响。内存高效利用支持虚拟内存、内存置换闲置数据可暂存硬盘突破物理内存大小限制运行程序。程序通用可加载程序基于虚拟地址编译无需绑定固定物理内存位置可灵活加载到内存任意位置。权限管控可对虚拟地址设置读、写、执行权限保护内核数据和程序代码安全。多进程可以干什么1. 实现并发任务几件事一起跑实现多任务并发任务。2. 故障隔离一个任务崩了不连累主程序每个进程内存独立、互相隔离。3. 充分利用多核 CPU 算力启多个进程操作系统可以把不同进程调度到不同 CPU 核心实现真正的并行计算4. 解耦业务程序模块化拆分模块之间使用IPC通信单个模块可以单独重启、升级进程的状态有哪些1. 新建创建态进程刚被创建PCB 进程控制块正在初始化资源还没分配完成还没进入就绪队列。2. 就绪态 Ready已经分配好了除CPU以外的所有资源万事俱备只等 CPU 调度一旦获得 CPU 立刻运行3. 运行态 Running进程占有 CPU正在处理器上执行代码单核 CPU 同一时刻只能有1 个进程处于运行态4. 阻塞态等待态 / 睡眠态Blocked进程等待某事件主动放弃 CPU。 例等待键盘输入、读磁盘数据、等待信号、等待锁。⚠️阻塞不能直接回到运行态事件完成后只能先回到就绪态。5. 终止态结束态Terminated进程执行完毕或被杀死。PCB 资源等待操作系统回收。子进程创建后拥有哪些资源子进程几乎复制父进程的地址空间采用写时复制 COW并不是立刻全部拷贝。进程地址空间虚拟内存代码段 (.text)、只读数据段 (.rodata)全局数据段 (.data/.bss)、堆用户栈初始父子指向同一块物理内存一旦任意一方修改数据操作系统才复制一份写时复制。文件描述符表父进程所有打开过的 fd (0 标准输入、1 标准输出、2 标准错误、文件、socket) 都会复制一份。父子共享同一个文件表项文件偏移量、文件状态标志一个进程修改文件读写偏移另一个也会受影响。用户 ID、组 ID会话 ID、进程组 IDuid、gid、有效用户 ID会话 sid、进程组 pgid控制终端信号处理方式每个信号的处理函数 (SIG_DFL / SIG_IGN / 自定义函数) 被复制。未处理的信号不会继承当前工作目录、根目录、文件权限掩码 umask环境变量表、资源限制 rlimit子进程不会继承的资源进程 ID (PID)自己全新的 pid父进程 pid (PPID) 是父进程 id父进程的锁互斥锁、文件锁不会继承闹钟定时器 alarm未到期的闹钟被清除父进程挂起的信号队列不会传给子进程每个进程独立的内核栈、PCB 进程控制块进程退出后发生了什么如何避免僵尸进程进程调用exit()/return/ 被信号杀死 → 进入终止状态用户空间资源堆、栈、文件描述符立刻释放但是 PCB进程控制块不会马上释放内核保留 PCB存放进程退出状态码等待父进程调用 wait () /waitpid ()读取退出状态如果父进程一直不调用 wait 回收子进程就变成僵尸进程 (Zombie状态 Z)避免僵尸进程方案 1父进程主动调用 wait () /waitpid ()阻塞回收方案 2父进程非阻塞轮询 waitpid方案 3信号处理等待SIGCHLD信号到来在信号回调函数回收子进程资源方案 4使用孙进程或退出父进程使得子进程可以被init收养会自动回收资源在子进程中需要执行一个外部可执行程序需要怎么做与原来子进程相比发生了什么变化子进程调用 exec 系列函数加载外部可执行程序常用函数execl、execlp、execv、execvp、execveexec 执行之后子进程发生了哪些变化✅ 会被替换、销毁掉的资源用户空间地址空间完全清空原来 fork 复制过来的 .text 代码段、.data、.bss、堆、用户栈全部丢弃载入新程序的代码、数据开辟全新堆栈原来子进程里面定义的变量、函数全部消失再也不能访问exec 成功旧程序被完全覆盖。✅保留不变继承下来的内核资源——不会被替换PID 进程 ID 不变最重要PID 永远不会变文件描述符 fd默认继承打开的文件、socket 还在可以用fcntl(fd, F_SETFD, FD_CLOEXEC)设置执行 exec 时自动关闭该文件描述符进程组 ID、会话 ID、控制终端用户 ID、组 IDuid,gid当前工作目录、根目录、umask 掩码信号处理方式原先自定义的信号处理函数全部重置为默认 SIG_DFL被忽略的信号 (SIG_IGN)保持忽略状态不变✅ 新生成、全新的资源新程序的代码段、数据段、堆、栈全新的信号栈新程序 main 函数的 argv 参数、环境变量execve 可以自定义环境什么是线程线程Thread操作系统能够进行 CPU 调度和分派的最小单位。进程资源分配的最小单位拥有独立地址空间、文件、堆内存线程CPU 调度执行的最小单位不拥有独立资源共用所属进程的资源对比项进程线程资源所有权拥有独立内存、地址空间共享所属进程资源切换开销大进程上下文切换小线程上下文切换隔离性进程之间完全独立互不影响一条线程崩溃整个进程直接崩溃通信进程间通信 IPC管道、共享内存、消息队列直接读写全局变量通信简单单线程任务排队做完一个再做下一个。多线程一个程序里同时开启多条执行流宏观上并发执行多个任务。注意单核 CPU 同一时刻只能跑 1 个线程靠 CPU 快速轮流切换线程制造 “同时运行” 的假象多核 CPU 才可以做到真正的并行。创建线程函数函数指针的作用创建线程函数原型如下int pthread_create( pthread_t *thread, //线程的tid号 const pthread_attr_t *attr, //线程属性默认NULL为不分离需要主动回收 void *(*start_routine) (void *), //重点线程函数指针 void *arg //传给线程执行函数的参数 );start_routine保存的是线程入口函数的地址操作系统新建出来的子线程启动之后就跳转到这个地址开始执行代码主线程执行main() 新建的子线程没有自带入口必须你告诉它去跑哪一个函数。这个 “告诉” 就是靠函数指针传过去。函数指针就是指向执行函数首地址的一个指针在这里直接填函数名。是因为函数名出现在这个创建线程的函数中的时候编译器会自动将函数名转化为函数指针实际上函数名只是一个代码字段的入口地址标签类似于汇编语言里loop标签用于跳转作用并不等于是一个指针指针是一种变量类型因此一个函数指针是可以操作指向任意同类型的函数的而这里的函数名只能代表自己这个函数。线程分离属性 detachstate1. 基础概念每个pthread线程都有一个属性分离属性 (detachstate)只有二种状态① 可接合态默认PTHREAD_CREATE_JOINABLE线程创建出来默认就是可接合线程退出后它的退出状态、返回值会被保留在内存中TCB 资源不会释放必须由别的线程调用pthread_join(tid, retval)join会等待线程结束 回收线程资源 获取返回值后果忘记 join → 线程变成僵尸线程资源泄漏② 分离态PTHREAD_CREATE_DETACHED线程退出时自动释放所有线程资源没人可以再 pthread_join 它不能调用pthread_join()调用会直接失败线程的返回值会直接丢弃无法获取一句话总结可接合资源等别人来回收分离线程干完活自己回收自己。什么是同步和互斥Linux提供的对应机制名称是什么什么是内核对象互斥 (Mutex‑Mutual Exclusion)目的解决资源竞争问题保护临界资源多个线程不能同时访问同一个共享资源同一时刻只允许一个线程进入临界区。目的排他、独占、抢资源、防乱改例子两个线程同时修改同一个全局变量加互斥锁保证一次只有一个线程修改。2. 同步 (Synchronization)目的控制线程执行先后顺序线程之间按约定次序运行线程 A 必须等线程 B 做完某件事之后A 才能继续执行。目的先后顺序、等待、通知、条件依赖例子生产者先放数据消费者才能取数据消费者没数据就要阻塞等待生产者。内核对象是由 Linux 操作系统内核管理、创建、维护的数据结构存放在内核空间用户程序不能直接访问它。用户进程只能拿到一个句柄 / 文件描述符 ID通过系统调用让内核替你操作这个对象。用户对象 VS 内核对象用户对象在你的程序 (用户空间) 内存里由应用代码管理。 例如pthread_mutex_t、普通局部变量、全局变量。注意pthread_mutex 互斥锁本质是用户态对象部分锁操作可以不陷入内核。内核对象在内核空间归操作系统管理。进程消亡内核对象不一定立刻销毁。Linux 常见内核对象举例进程 PCB (task_struct)文件、文件描述符 fd管道System V 信号量、消息队列、共享内存段信号、定时器套接字 socket什么是死锁死锁的四个必要条件如何避免死锁发生死锁如何解决死锁两个或多个线程 / 进程各自拿着对方需要的资源又互相等待对方释放资源所有人都被永久阻塞谁也无法继续往下执行。互斥条件资源同一时刻只能被一个线程占用别人想要必须等待。锁就是典型互斥资源。请求与保持条件占有且等待线程已经占有了至少一个资源又去请求另一个被别人占有的资源并且不释放自己手上已经拿到的锁。不可剥夺条件别人已经拿到的资源不能被其他线程强行抢走只能由持有者主动释放。 互斥锁不能被别的线程强制解锁。环路等待条件线程之间形成一条等待闭环A 等 B、B 等 C、C 等 A构成一个资源等待的环形链。⚠️注意四个条件只是必要条件不是充分条件。四个条件同时具备不一定死锁但是死锁发生的时候四个条件一定全部成立。需要解决死锁破坏死锁的四个必要条件即可1. 破坏「互斥条件」尽量不用互斥使用无锁方案。大部分临界资源必须互斥访问这条很难破坏很少用。2. 破坏「请求与保持」方案线程一次性申请所有需要的锁要么全部拿到一把都不拿 不允许拿一把锁之后再去申请第二把。3. 破坏「不可剥夺」如果申请不到新锁主动放弃自己手里已经持有的所有锁过一会再重新申请。4. 破坏「环路等待」⭐最常用、最简单给所有锁规定统一的申请顺序所有线程必须按相同顺序拿锁。进程间通信方式1. 管道 Pipe匿名管道特点半双工单向通信只能用于有血缘关系进程(父子、兄弟进程)数据一次性读取读完就没存在内核缓冲区。使用pipe()系统调用限制没有名字只能通过继承文件描述符传递。2. 命名管道 FIFO有名管道特点半双工无血缘进程之间也可以通信在磁盘上生成一个管道文件文件仅起标识数据仍然存放内核缓冲区。使用mkfifo()创建管道 (Pipe)、FIFO 本质都是字节流没有消息边界。3. 消息队列 Message QueueSystem‑V IPC特点内核创建的队列进程间传递带类型的消息块数据有边界消息读取可以按消息类型读取不一定要先进先出消息读完不会自动消失。内核对象生命周期随内核进程退出队列不会销毁。APImsgget、msgsnd、msgrcv、msgctl4. 共享内存 Shared Memory速度最快的 IPC 方式多个进程同一块物理内存映射到自己的虚拟地址空间。缺点本身不带同步互斥机制必须额外搭配信号量、互斥锁来保护数据。APIshmget、shmat、shmdt、shmctl5. 信号量 Semaphore信号量不是用来传递数据它是同步互斥工具用来解决进程 / 线程资源竞争。System‑V 信号量一组信号量集合常用于配合共享内存做进程同步。POSIX 有名 / 无名信号量线程、进程都能用。APIsemget、semop、semctl6. 信号 SignalLinux 异步通信方式。进程收到信号后中断当前工作执行信号处理函数。属于简单通知机制只能传递一个信号编号不能携带大量数据。例子SIGKILL、SIGINT(ctrlc)APIkill()、raise()、signal()、sigaction()7. 套接字 Socket网络 IPC支持本机进程通信 跨主机网络通信。本地域套接字 (Unix‑domain socket)仅本机进程通信不走网卡效率高。TCP/UDP socket跨电脑通信。通信方式能否传大量数据有无同步能力适用进程特点匿名管道 pipe中等自带读写阻塞父子进程单向血缘进程有名管道 fifo中等自带读写阻塞任意进程磁盘管道文件消息队列较大自带阻塞任意进程带消息类型面向消息共享内存最大无同步需额外加锁任意进程速度最快信号量不能传数据专门做同步互斥-锁工具信号 signal极少 (仅编号)异步通知任意进程简单事件通知socket大协议控制本机 / 跨主机网络通信匿名管道最大限制没有名字无法被外部非亲缘进程打开只能 fork 继承 fd。FIFO 就是为了解决匿名管道不能跨非亲缘进程而生。System‑V 的三种 IPC消息队列、共享内存、信号量依靠key值标识完全不需要血缘。信号只要拿到 PID不管有没有血缘都可以发送。有名管道的读阻塞、写阻塞和管道破裂的条件操作条件行为open (O_RDONLY, 阻塞)无写端打开读端阻塞open (O_WRONLY, 阻塞)无读端打开写端阻塞read()缓冲区空写端存活读阻塞read()缓冲区空所有写端关闭read 返回 0write()管道缓冲区满读端存活写阻塞write()所有读端已经关闭执行 write发送 SIGPIPE管道破裂无名管道 (pipe) 和 有名管道 (FIFO)1. 无名管道 pipeint pipe(int fd[2]);没有文件名、没有磁盘节点只存在于内存内核缓冲区只能用于具有血缘关系的进程通信父子进程、兄弟进程fork 出来半双工通信一端读、一端写两个文件描述符fd[0]读端fd[1]写端生命周期随进程所有相关进程关闭管道管道自动销毁只能单向数据流若要双向通信需要创建 2 条管道不能通过 open 打开只能由 pipe 创建再 fork 共享文件描述符2. 有名管道 FIFOint mkfifo(const char *pathname, mode_t mode);有文件名存在于文件系统是一个特殊管道文件磁盘上仅占 inode数据还是放内核内存缓冲区无血缘限制任意两个进程只要知道管道文件名就可以通信半双工同样单向双向通信也要两条 FIFO生命周期持久创建之后文件一直存在调用unlink()才删除进程退出管道文件不会自动消失使用方式mkfifo创建 →open打开 → read/write 读写 → close 关闭 → unlink 删除对比项无名管道 pipe有名管道 FIFO存储位置内核缓冲区 (内存)无文件文件系统有文件名数据仍在内核缓冲区通信进程必须有血缘关系 (父子 / 兄弟)任意进程无血缘要求创建函数pipe()mkfifo()打开方式fork 继承文件描述符不能 open使用open()打开管道文件生命周期所有进程关闭后管道消失文件永久存在需要 unlink 删除访问路径无路径名有路径名阻塞特性读写都会阻塞 (默认)读写都会阻塞 (默认)适用场景父子进程快速 IPC互不相关进程之间通信Linux中什么是信号最常用的信号有哪些信号Signal是 Linux/Unix 下一种异步通信方式属于进程间通信 (IPC)。信号本质就是一个数字编号用来通知进程发生了某个事件。信号是异步进程不知道信号什么时候到来信号可以在进程运行的任意时刻打断它。谁可以发信号内核、另一个进程、键盘快捷键CtrlC。进程收到信号后有 3 种处理方式默认动作系统预设行为终止、暂停、忽略、核心转储忽略信号收到信号什么也不干signal() / sigaction()捕获信号自定义处理注册一个信号处理函数收到信号就执行自己写的回调函数⚠️ 有两个信号不能被捕获、不能被忽略SIGKILL(9)、SIGSTOP(19)信号名编号含义触发来源默认动作SIGINT2中断信号键盘Ctrl C终止进程SIGQUIT3退出信号键盘Ctrl \终止 生成 core 核心转储文件SIGKILL9强制杀死进程kill -9 进程号立刻终止不可捕获 / 忽略SIGSEGV11段错误非法内存访问代码越界、空指针解引用终止 core 转储SIGALRM14闹钟信号alarm()定时函数终止进程SIGTERM15优雅终止信号kill 进程号不带‑9终止进程可以被捕获忽略SIGSTOP19暂停进程CtrlZ、kill‑19暂停不可捕获 / 忽略SIGCONT18继续运行暂停进程kill -18恢复被暂停的进程运行SIGCHLD17子进程状态改变子进程退出子进程结束发给父进程默认忽略收到信号之后可以有哪些处理方式可以修改处理动作吗默认处理SIG_DFLLinux 内核预先规定好该信号的默认行为一共就 5 类终止进程退出程序如 SIGINT (2)、SIGTERM (15)终止 生成 core 转储文件方便调试崩溃如 SIGQUIT (3)、SIGSEGV (11)暂停进程进程挂起停止运行如 SIGSTOP (19)继续运行唤醒暂停进程如 SIGCONT (18)忽略信号收到信号什么都不做如 SIGCHLD (17) 默认忽略2. 忽略信号SIG_IGN进程收到信号直接丢掉完全不响应。例外SIGKILL (9)、SIGSTOP (19) 绝对不能忽略3. 捕获信号自定义信号处理函数程序员写一个回调函数当信号到来时内核打断当前代码跳转去执行你写的信号处理函数处理完再回到原来代码继续运行。例外SIGKILL、SIGSTOP 不能捕获修改处理动作注册对应信号的处理函数typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);共享内存是什么共享内存和管道的区别共享内存Shared Memory是Linux 进程间通信 IPC 的一种方式。通过在内核开辟一块物理内存缓冲区映射到多个不同进程的虚拟地址空间。 多个进程都可以直接读写这同一块内存进程之间不需要内核转发拷贝数据。对比项管道 (pipe/FIFO)共享内存 (System‑V)数据传递方式两次拷贝 (用户→内核→用户)无拷贝直接访问物理内存速度慢最快 IPC同步互斥自带同步阻塞机制内核管理无同步需要程序员手动加锁 / 信号量数据特性字节流读走之后数据就消失数据保留可反复读写通信方向半双工 (单向)全双工双向读写进程关系pipe要有血缘FIFO 无血缘任意进程无血缘限制生命周期管道所有进程关闭就销毁FIFO文件需 unlink 删除共享内存段手动 shmctl 删除进程退出不会自动删适用场景少量数据、简单顺序传输大批量数据高速交换管道通信时数据需要从发送进程拷贝到内核缓冲区再从内核拷贝到接收进程共享内存将同一块物理内存映射到多个进程虚拟地址进程直接读写内存省去两次拷贝效率更高。管道自带内核提供的同步阻塞机制共享内存没有同步机制必须额外使用信号量等方式实现进程同步防止竞争访问。管道的数据一旦被读取就消失共享内存的数据可以被多个进程反复读取。Linux进程调度算法实时进程Real‑time优先级高对响应时间要求苛刻。普通进程CFS完全公平调度交互式程序、后台任务。1、实时进程调度算法2 种实时进程优先级1‑99数字越大优先级越高。1SCHED_FIFO 先来先服务FIFO非时间片轮转一旦进程获得 CPU就一直运行直到主动放弃 CPU、阻塞、被更高优先级进程抢占同优先级先就绪先运行低优先级进程永远抢不过高优先级2SCHED_RR 时间片轮转带时间片的 FIFO同优先级进程轮流执行分配固定时间片时间片用完放到同优先级队列末尾高优先级依然可以随时抢占实时进程可以抢占普通进程2、普通进程调度CFS 完全公平调度器Linux 2.6.23 之后默认调度策略SCHED_OTHERCFS 核心思想不按固定时间片给每个进程分配公平的 CPU 运行时间。引入关键概念虚拟运行时间 vruntime进程每跑一会虚拟时间就累加优先级高nice 值小→ vruntime 增长慢能获得更多 CPU 时间红黑树平衡二叉搜索树就绪进程全部放在红黑树里每次选 vruntime 最小的进程上 CPU 运行nice 值‑20 ~ 19默认 0nice 越小 → 优先级越高分得 CPU 越多锁的机制锁类型核心特点典型场景互斥锁 Mutex同一时间仅 1 个线程访问谁上锁谁解锁普通共享资源、队列保护递归锁同一个线程可重复上锁递归函数读写锁 rwlock读共享写独占读多写少数据自旋锁抢不到锁就空转不休眠锁占用时间极短内核信号量 Semaphore计数可跨线程释放锁资源限流、进程间同步