进程及其相关函数

进程及其相关函数 什么是进程什么是程序程序静态的是有序指令的集合进程动态的 程序一次执行的过程伴随着资源的分配与释放进程定义进程是*操作系统中的一次执行过程它是操作系统进行资源分配和调度的基本单位只要用户执行了一个程序在内核空间就会创建一个task_struct结构体这个结构体就代表当前的进程进程运行产生的所有信息如进程状态、PID、内存指针等都被放到了这个结构体中保存着进程执行完之后task_struct会被内核回收如果异常崩溃内核也会清理资源避免僵尸进程进程的核心特征1.动态性进程的实质是程序在多道程序系统中的一次执行过程进程是动态产生动态消亡的2.并发性任何进程都可以同其他进程一起并发执行3.独立性进程是一个能独立运行的剧本单位同时也是系统分配资源和调度的独立单位4.异步性由于进程间的相互制约使进程具有执行的间断性即进程各自独立、不可预知的速度向前推进进程的特征进程依赖于程序而存在进程是动态的程序是静态的进程是操作系统进行资源分配和调度的一个独立单位每个进程都有独立的地址空间程序与进程的区别对比维度程序Program进程Process定义是静态的指令集合存储在磁盘上的可执行文件(如.exe等)为执行时不占用系统资源是程序的动态执行过程是程序运行时的实例 占用CPU、内存、I/O等系统资源有生命周期存在形式静态存在以文件形式长期存储动态存在随程序启动而创建随执行结束而销毁资源占用不占用CPU、内存等运行时资源仅占用磁盘存储空间占用CPU、内存、文件句柄、网络端口等系统资源 有独立的地址空间状态无状态(仅为指令和数据的静态集合)有状态(如运行态、停止态等)状态会随系统调度变化独立性多个程序之间互相独立无直接关联 (除非通过文件等外部方式交互)每个进程有独立的地址空间和资源进程间默认不共享数据(需通过进程间通信机制实现)示例如硬盘上的浏览器.exe文件双击浏览器.exe后运行的浏览器窗口程序与进程的关联1.进程是程序的执行过程一个程序可以对应多个进程(如 同时打开多个浏览器窗口)2.程序是进程的模版进程是程序的运行态体现3.进程的执行过程本质是按程序指令逐步推行程序的逻辑决定进程的行为4.同一程序的多个进程共享程序的代码段(只读)到各自拥有数据段(独立)5.关闭进程后程序文件仍存在于磁盘删除程序文件后已运行的程序可继续执行(直到依赖文件时出错)进程的组成进程是由三个部分组成的分别为进程控制块(PCB)、文本段、数据段1.进程控制块(PCBProcess Control Block)PCB是操作系统用于管理和控制进程的核心数据结构每个进程在内存中都有唯一的PCB它记录着进程的所有关键信息(如进程识别信息、进程状态信息、内存管理信息、资源占用信息等)是操作系统感知进程存在的依据2.文本段是进程中存储可执行文件指令的内存区域即程序编译后生成的机器码为了防止程序意外修改自身指令文本段通常被设置为只读如果多个进程运行同一个程序他们可以共享同一份文本段节省内存空间编译后的指令数量是固定的。文本段的大小在进程运行期间不会改变3.数据段适用于存储进程运行过程中需要的数据根据数据初始化状态和可修改性分为初始化数据段、未初始化数据段、堆、栈MMU内存管理单元1、保护物理内存空间2、增大空间的利用率3、将固体硬盘当作内存使用进程的分类Linux系统下有三种进程类型分别是交互进程、批处理进程和守护进程1.交互进程定义由shell维护需要用户在终端手动启动的进程通过shell和用户进行交互(用户交互输入、输出)特点依赖终端环境通常在前台运行生命周期与用户操作相关2.批处理进程定义无需用户实时交互、优先级较低按预设任务序列自动运行的程序通常在后台执行特点优先级较低(避免占用关键资源)多通过脚本或任务调度工具启动3.守护进程定义系统启动时自动运行的后台进程独立于终端通常随系统启动而启动随系统关闭而终止用于提供持续服务特点运行时脱离终端进程的状态**运行状态(**R,TASK_RUNNING)也称为可运行状态就是处于运行状态的进程正在CPU执行或等待CPU调度**可中断睡眠状态(**S,TASK_INTERRUTIBLE)进程在等待某个事件(如等待I/O操作、等待特定信号等)完成可以被信号唤醒**不可中断睡眠状态(**D,TASK_UNINTERRUTIBLE)进程正在等待关键硬件操作完成不可以被信号中断确保数据完整性**停止状态(**T,TASK_STOPPED)进程被暂停执行通常是收到了特定的信号(如SIGSTOP(暂停进程))或者在调试程序时被调试器暂停可被信号恢复执行(如收到SIGCONT进入就绪)**僵尸状态(**Z,EXIT_ZOMBIE)进程已终止但资源未完成回收等待父进程进行回收**死亡态(**X,EXIT_DEAD)进程彻底终止资源全部释放是一个瞬间状态几乎无法观测到进程的附加状态在Linux系统中进程除了基础状态之后还会有一些附加状态标记用于补充说明进程的特殊属性(前台进程组)表示进程属于前台进程组与当前终端直接关联用户可以通过终端输入直接控制l(多线程进程)表示进程包含线程(LWP)通常对应多线程程序N(低优先级进程)表示进程的nice值为正数(NI0)优先级较低CPU资源优先给其他进程(高优先级进程)表示进程的nice值为负数(NI0)优先级较高会优先获取CPU资源s(会话leader进程)表示进程是会话首进程负责管理一个会话中的其他进程L(进程持有锁)表示进程正在持有某个文件锁或者是资源锁可能是在等待其他进程释放锁资源进程间状态图进程PIDPID(process id)是进程的唯一身份标识操作系统给每个进程分配一个数字编号(比如微信的PID可能是1234浏览器的可能是6789)PID在Linux中的规则取值数量限制系统能创建的进程总数有限可通过sysctl kernel.pid_max查看上限查看机制每个运行的进程对应/proc/[PID]目录(如PID80320对应/proc/80320)目录内的status文件存储进程详情(PID、名称、运行状态等)特殊PID特殊PID进程名称作用与职责0号idle系统启动后的第一个进程 负责空闲CPU调度(无其他进程运行时idle进程占用CPU)1号init由0号进程创建 负责初始化系统硬件、启动服务、回收孤儿进程(如 关机时清理残留进程)2号kthreadd负责内核进程调度(管理系统后台的内核级任务如磁盘IO、内存回收)PS命令功能用于查看当前系统中进程的快照信息包括进程的PID(进程标识符)、所属用户、占用CPU和内存资源情况等常用具体命令选项功能ps -ef-e列出系统中所有进程 -f以全格式显示进程信息查看当前所有进程ps -aux-a显示所有终端进程 -u以用户资源视角为中心的格式 -x显示无终端进程(如后台服务)展示进程资源占用百分比(如占用CPU百分比、占用内存百分比等)ps -eo 字段1,字段2…-e列出系统中所有进程 -o自定义输出格式精准提取字段(如pid,comm)ps -l-l长格式以长格式查看当前终端进程详细信息top命令功能动态的显示系统中各个进程的资源使用情况可以实时查看进程的CPU使用率、内存使用量、线程数等并且能按照不同的指标进行排序htop命令功能是一个交互式的进程查看工具相比top具有更友好的用户界面支持鼠标操作可以更加直观地展示进程树结构kill命令功能用于向进程发送信号常见的就是用来终止进程。通过指定进程的PID发送特定的信号让进程执行相应的操作命令格式信号编号对应信号名称功能说明kill -l--列出系统中所有可用的信号及其编号kill -2 pid2SIGINT向指定进程发送中断信号kill -9 pid9SIGKILL强制终止指定进程kill -19 pid19SIGSTOP暂停指定进程的运行kill -18 pid18SIGCONT使已暂停的进程恢复运行killall命令功能根据进程名来终止进程当系统中有多个同名进程需要同时终止时killall就非常方便命令格式信号编号对应信号名称功能说明killall -进程名--终止所有名为进程名的进程(默认发SIGTERM)killall -9 进程名9SIGKILL强制终止所有同名进程killall -i 进程名--终止前逐一询问确认(交互模式)pstree命令功能以树结构显示进程之间的父子关系从系统初始化init开始直观地展示整个系统进程的层次机构bg/fg命令命令格式功能说明注意事项bg 作业号将挂起的进程放到后台继续运行若不指定作业号默认操作最近一个被挂起的作业 作业号可通过jobs -l命令查看获取fg 作业号把后台运行的进程 放到前台终端运行恢复与终端交互若不指定作业号默认将最近一个后台作业调至前台调至前台后进程会占据终端输入输出直到进程结束或再次被暂停nice/renice命令命令功能说明注意事项nice启动进程时设置优先级 未指定值时默认优先级为10nice -n 优先级值 要执行的命令renice调整已运行进程优先级 可针对单进程、用户所有进程进行调整renice 新优先值 -p 进程PID (单进程) renice 新优先值 -u 用户名 (用户所有进程)进程相关函数1.创建进程fork函数所需头文件#includesys/types.h#includeunistd.h原型pid_tfork(void);功能复制当前进程创建子进程 参数无 返回值成功返回大于0的整数(父进程收到子进程的pid)成功返回0(子进程)失败子进程没有被创建父进程收到-1并重置错误码2.进程号获取函数1getpid函数 所需头文件#includesys/types.h#includeunistd.h原型pid_tgetpid(void);功能获取当前进程自身的进程号 参数无 返回值返回当前进程自身的进程号 2getppid函数 所需头文件#includesys/types.h#includeunistd.h原型pid_tgetpid(void);功能获取当前进程的父进程的进程号 参数无 返回值返回当前进程的父进程的进程号3.进程退出函数1exit函数 所需头文件#includestdlib.h原型voidexit(intstatus);功能正常终止进程且status0377(按位与运算后的值)会被返回给父进程 C标准规定了两个常量EXIT_SUCCESS和EXIT_FAILURE它们可被传递给exit()函数 参数status退出状态值 返回值无 2_exit函数 所需头文件#includeunistd.h原型void_exit(intstatus);功能立即终止进程且status0377(按位与运算后的值)会被返回给父进程 C标准库为exit函数规定了EXIT_SUCCESS和EXIT_FAILURE常量由于七八值通常为0和1 也可传递给_exit兼容使用但_exit本身是系统调用不依赖这些宏 参数status退出状态值 返回值无4.资源回收函数1wait函数 所需头文件#includesys/types.h#includesys/wait.h原型pid_twait(int*wstatus);功能父进程阻塞等待任意一个子进程结束若没有子进程结束wait会一直等待 当有子进程终止时它会回收该子进程的资源并通过wstatus获取子进程的退出状态值 参数wstatus用于存储子进程的退出状态值的内存空间首地址 wstatus若为空(NULL),表示忽略子进程退出时的状态 wstatus若不为空表示将子进程退出状态写入该指针指向的内存供父进程后续用宏解析 返回值成功返回终止子进程的PID 如果调用进程没有子进程或者调用出错返回-1同时会设置errno来指示错误原因wait函数状态解析宏1.子进程正常退出(1)WIFEXITED(wstatus)若子进程是正常终止(调用exit()/_exit()或从main()返回)则返回true(2)WEITSTATUS(wstatus)仅当WIFEXITED为true时使用返回子进程的退出状态码(即exit(n)中的n取低8位)2.子进程被信号终止(1)WIFSIGNALED(wstatus)若子进程是被信号终止(如SIGKILL等)则返回true(2)WTERMSIG(wstatus)仅当WIFSIGNALED为true时使用返回导致子进程终止的信号编号(如9对应SIGKILL)(3)WCOREDUMP(wstatus)仅当WIFSIGNALED为true时使用若子进程终止时产生了核心转储文件则返回true3.子进程被信号暂停(1)WIFSTOPPED(wstatus)若子进程是被信号暂停(仅在使用WUNTRACED标志或子进程被跟踪时有效)则返回true(2)WSTOPSIG(wstatus)仅当WIFSTOPPED为true时使用返回导致子进程暂停的信号编号(如19对应SIGSTOP)4.子进程被恢复运行WIFCONTINUED(wstatus)若子进程是通过SIGCONT信号恢复运行则返回true2waitpid函数 所需头文件#includesys/types.h#includesys/wait.h原型pid_twaitpid(pid_tpid,int*wstatus,intoptions);功能父进程等待子进程状态变化并获取其状态信息 允许父进程精准控制子进程的范围可通过options调整等待行为(如阻塞/非阻塞)参数pid指定要等待的子进程-1:等待任何进程组id与进程pid相等的绝对值的子进程-1:等待任何子进程资源0:等待如何进程组id与调用进程相同的进程0:等待指定pid的子进程 wstatus用于存储子进程的退出状态值的内存空间首地址 wstatus若为空(NULL),表示忽略子进程退出时的状态 wstatus若不为空表示将子进程退出状态写入该指针指向的内存供父进程后续用宏解析 options标志位(默认模式是阻塞等待模式 options0)返回值成功返回终止子进程的PID 如果设置非阻塞且没有子进程退出会返回0如果调用进程没有子进程或者调用出错返回-1同时会设置errno来指示错误原因options参数选项常量作用具体行为典型应用场景0实现阻塞等待父进程暂定直到子进程状态变化常规子进程同步等待(如批处理)WNOHANG实现非阻塞等待调用waitpid时若指定子进程进程未退出 函数立即返回0父进程无需阻塞等待父进程需轮询监控子进程状态(如后台任务管理)WUNTRACED捕获子进程暂停状态当子进程因信号(如SIGSTOP)停止运行时waitpid返回该子进程PID调试场景 需处理子进程暂停逻辑的程序WCONTINUED捕获子进程恢复运行状态已停止的子进程通过SIGCONT信号恢复运行时waitpid返回该子进程PID需跟踪子进程完整生命周期 (暂停-恢复)场景文件描述符重定向函数1.dup函数所需头文件#includeunistd.h原型intdup(intoidfd);功能复制一个已有的文件描述符 新文件描述符采用最小未分配的原则分配新的文件描述符 参数oldfd:旧的文件描述符 返回值成功返回新的文件描述符 失败返回-1重置错误码2.dup2函数所需头文件#includeunistd.h原型intdup2(intoldfd,intnewfd);功能复制一个已有的文件描述符oldfd 新文件描述符使用newfd中指定的文件描述符 参数oldfd:旧的文件描述符 newfd:新的文件描述符 返回值成功返回新的文件描述符 失败返回-1重置错误码父子进程内存空间1.父进程在fork产生子进程时用到了写时拷贝的原则2.如果父子进程中对内存进行读操作那么内存不会被重新映射物理内存3.如果父子进程中的任意一方对内存有写操作时才会重新映射到新的物理内存练习1使用getpid获取子进程的id号再通过kill命令杀死子进程观察子进程的进程状态练习2使用非阻塞方式回收子进程资源 并查看父进程状态练习31、创建子进程2、使用文件IO父进程向文件写入内容子进程循环读取3、当父进程输入quit子进程退出父进程回收资源并退出注意在父进程写入之前以及写入之后都要使用lseek实现偏移strncmpbuf“quit”4exec函数簇所需头文件#includeunistd.h函数原型intexecl(constchar*pathname,constchar*arg,.../* (char *) NULL */);intexeclp(constchar*file,constchar*arg,.../* (char *) NULL */);intexecv(constchar*pathname,char*constargv[]);intexecvp(constchar*file,char*constargv[]);intexecvpe(constchar*file,char*constargv[],char*constenvp[]);intexecle(constchar*pathname,constchar*arg,.../*, (char *) NULL, char *const envp[] */);l参数以列表方式进行传入 v参数放在数组中向函数传入数组地址 p第一个参数传递的类型不带p传递的是绝对路径加文件名 带p则会去相应的环境变量里面去找可执行文件 e会传递环境变量 函数返回值成功无返回(进程已被新程序替换)失败-1并设置errno来重置错误码 获取环境变量char*getenv(constchar*name);练习exec函数簇的使用 作业1、思考fork();fork();printf(hello\n)会打印几个hello共有几个进程2、思考fork();fork();printf(hello)会打印几个hello共有几个进程execlp函数所需头文件#includeunistd.h原型intexeclp(constchar*file,constchar*arg,.../*(char *) NULL */);功能在环境变量PATH所指定的目录中查找file参数指定的程序文件 并使用该程序替换当前进程的映像 参数file:要执行的程序文件名(无需提供完整路径系统会在环境变量指定的目录中查找该程序)arg:传递给新程序的参数列表需要逐个列出参数并且以NULL作为结束标志 返回值成功无返回(进程已被新程序替换)失败-1并设置errno来重置错误码execvp函数所需头文件#includeunistd.h原型intexecvp(constchar*file,char*constargv[]);功能在环境变量PATH所指定的目录中查找file参数指定的程序文件 并使用该程序替换当前进程的映像 参数file:要执行的程序文件名(无需提供完整路径系统会在环境变量指定的目录中查找该程序)argv:指向字符指针数组的指针(数组最后一个元素必须是NULL)返回值成功无返回(进程已被新程序替换)失败-1并设置errno来重置错误码进程组概念 进程组是一个或多个进程的集合每个进程组有一个唯一的进程组ID注意只要在某个进程组中有一个进程存在则该进程组就存在这与其组长进程是否终止无关。相关函数1、查看进程组id所需头文件 #includesys/types.h#includeunistd.h原型pid_tgetpgrp(void);返回值返回调用者的进程组ID常用失败返回-1并返回错误码 原型pid_tgetpgid(pid_tpid);返回值返回指定pid的进程组id失败返回-1并返回错误码2、设置进程组id所需头文件#includesys/types.h#includeunistd.h原型intsetpgid(pid_tpid,pid_tpgid);功能将pid进程的进程组设置为pgid 返回值成功返回0失败返回-1特殊进程1.孤儿进程定义当一个进程的父进程先于它终止该进程就会成为孤儿进程产生原因父进程意外崩溃、被强制终止(如kill -9)或父进程主动退出但未正确处理子进程特点1.失去父进程后会被系统的祖先进程(linux中是ini或tsystemPID1)收养2.仍能正常运行完成自身任务后会正常终止不会占用额外资源影响本身是系统正常处理机制的结果无负面影响反而避免了进程成为无主进程例如若父进程因bug崩溃子进程被init收养后可继续提供服务2.僵尸进程定义进程终止后内核未释放其进程控制块(PCB)该进程状态变为Z(僵尸态)成为僵尸进程产生原因父进程未调用wait()或waitpid()等系统调用读取子进程的退出状态(如退出码、终止原因)导致内核无法回收PCB特点1.已终止运行不再执行任何代码但PCB仍只用内存资源(如PID、退出状态)2.不占用用户空间的内存数据段但内核空间的PCB资源仍被占用且无法使用影响1.系统PID数量有限大量僵尸进程会耗尽PID资源导致新进程进行无法创建2.不占用用户空间的内存数据段但内核空间的PCB资源仍被占用且无法使用处理方式预防父进程调用wait()/waitpid()主动回收子进程状态清除kill -9无法直接杀死僵尸进程(其已终止无法运行实体);父进程未处理可终止父进程(僵尸进程被1号进程收养1号进程会定期回收其PCB);3.守护进程定义运行在后台的特殊进程独立于控制终端用于持续提供系统服务设计目的不受用户登录/注销影响长期稳定运行(如网络服务、定时任务)特点1.脱离终端无控制终端避免终端关闭导致进程终止2.后台运行通过ps命令查看状态通常为S(休眠)名称多以d结尾(如httpd、systemd)3.父进程为1号进程启动后会与原本父进程脱离被1号进程收养4.环境干净默认不继承终端信号工作目录通常为\文件描述符(如stdin/stdout/stderr)被关闭或重定向到/dev/null守护进程创建流程1.父进程fork()后退出子进程成为孤儿进程(被1号收养)2.子进程调用setsid()函数创建新会话脱离原终端控制3.调用chdir()函数改变进程的工作目录到一个不会被删除和卸载的目录下4.使用umask()函数修改创建文件的掩码5.关闭所有从父进程继承过来的文件描述符集合6.使用dup()或者是dup2()将标准输入、标准输出、标准错误都重定向到文件中7.开启自己的服务setsid函数所需头文件#include sys/types.h #include unistd.h 原型pid_t setsid(void); 功能创建一个新会话并让调用该函数的进程成为这个新会话的首进程同时也成为新进程组的组长 参数无 返回值成功返回调用进程的新会话标识符 失败-1重置错误码chdir函数所需头文件#include unistd.h 原型int chdir(const char *path); 功能将调用进程的当前路径修改为参数所指定的目录 参数path新的工作目录 返回值成功0 失败-1重置错误码umask函数所需头文件#include sys/types.h #include sys/stat.h 原型mode_t umask(mode_t mask); 功能修改进程文件的掩码 参数mask新的掩码 实际文件权限 ~mask 0666 实际目录权限 ~mask 0777 返回值总是成功返回之前的掩码三者的核心区别对比维度孤儿进程(Orphan Process)僵尸进程(Zomble Process)守护进程(Daemon Process)定义父进程先于子进程退出 子进程被1号进程收养的进程子进程先退出 但父进程未调用wait/waitpid回收其PCB导致进程状态为Z的进程脱离控制终端、在后台长期运行的系统服务进程(如sshd、nginx)产生原因父进程意外退出或主动退出 未等待子进程子进程退出后 父进程未调用wait/waitpid回收其退出状态程序主动调用setsid等函数创建 脱离终端并后台运行进程状态正常运行状态PPID1僵尸状态标记为后台运行状态无控制终端名称常以d结尾(如httpd)资源占用占用正常进程资源(代码/数据/PCB)仅占用PCB资源(PID、退出状态)占用正常进程资源长期稳定运行危害无直接危害 被1号进程正常管理大量积累会耗尽PID资源 导致新进程无法创建无危害是系统正常运行的必要服务(如网络服务、定时任务)处理方式无需手动处理 1号进程会自动回收其资源父进程调用wait/waitpid主动回收 终止父进程由1号进程回收无需处理随系统启动而启动、终止而终止可通过systemctl等工具管理典型场景父进程未等待子进程就退出 如脚本中后台运行的子进程父进程长期运行且未处理子进程退出 如服务器程序漏洞系统服务(如sshd、crond、nginx)