ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入解析Linux进程管理

深入解析Linux进程管理 一.进程与程序程序存储在磁盘上可执行的指令的集合是一个文件。进程程序一次执行的过程伴随着资源的分配与释放消耗CPU资源、内存资源、时间片(CPU给进程分配运行的时间)。两者区别程序是静态的一般存放在磁盘上没有执行的概念。进程是动态的运行在内存中并伴随着资源的分配与释放。程序与进程的构成C语言运行时内存分布程序进程构成text代码段rodata只读数据段data数据段bss未初始化的段text代码段rodata只读数据段data数据段bss未初始化的段堆、栈、系统相关的信息命令行参数、环境变量区别只有用户数据段和正文段可以使用size命令查看程序构成不仅有用户数据段和正文段还多了系统数据段包括堆、栈和系统相关的信息二. 虚拟地址与物理地址地址空间32位的系统地址空间2^324G64位的系统地址空间2^648G虚拟地址和物理地址的由来——地址空间不隔离虚拟地址到物理地址的映射不同的进程虚拟地址可能相同TLB为快表主要用于加速MMU将虚拟地址转换为物理地址的速度本质上是一块高速缓存。三.Linux系统进程类型交互进程定义由shell维护需要用户在终端手动启动的进程通过shell和用户进行交互(用户交互输入/输出)特点依赖终端环境通常在前台运行生命周期与用户操作相关批处理进程定义无需用户实时交互优先级比较低按预设任务序列自动运行的进程通常在后台执行特点优先级较低(避免占用关键资源)多通过脚本或任务调度工具启动守护进程定义系统启动时自动运行的后台进程独立于终端通常随系统启动而启动随系统关闭而终止用于提供持续服务特点运行时脱离终端四.进程的状态常见状态运行状态(R)也称之为可运行状态就是处于运行状态的进程正在CPU执行或等待CPU调度可中断睡眠状态(S)进程在等待某个事件(如等待I/O操作、等待特定信号等)完成可被信号唤醒不可以中断睡眠状态(D)进程正在等待关键硬件操作完成不能被信号中断确保数据完整性停止状态(T)进程被暂停执行通常是收到了特定的信号(如SIGSTOP(暂停进程))或者在调试程序时被调试器暂停可被信号恢复执行(如SIGCONT)僵尸状态(Z)进程已终止但资源未被回收等待父进程进行回收死亡态(X)进程彻底终止资源全部释放是一个瞬间状态几乎无法观测到附加状态(前台进程组)表示进程属于前台进程组与当前终端直接关联用户可以通过终端输入直接控制l (多线程进程)表示进程包含线程(LWP)通常对应多线程程序N(低优先级进程)表示进程的nice值为正数(NI 0)优先级较低CPU资源优先给其他进程(高优先级进程)表示进程的nice值为负数(NI 0)优先级较高会优先获取CPU资源s(会话leader进程)表示进程是会话首进程负责管理一个会话中的其他进程L(进程持有锁)表示进程正在持有某个文件锁或者是资源锁可能在等待其他进程释放锁资源转化图1.我们利用fork()函数创建了一个进程,此时处于就绪态2.进程通过进程调度资源来抢占CPU,调度完成就会进入运行态3.CPU此时会给进程分配时间片,时间片消耗完后进入等待态4.等待态有两种情况,可中断与不可中断,它们都可以回到就绪态5.程序在运行的时候,若是收到了一个SIGSTOP信号,则会变成停止态6.停止态的时候如果收到SIGCONT信号,则会回到就绪态7.当程序正常结束的时候,或者被SIGKILL信号杀死的时候我们程序由于有一些资源没有被释放,变成僵尸态。一般僵尸态若不指定回收对象会被Init(1)进程回收注意新版ubuntu系统中,init进程被systemd进程替代,他们进程号都为1特殊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发送特定的信号让进程执行相应的操作比如终止、暂停等等信号名含义默认操作SIGINT该信号在用户键入INTR字符通常是CtrlC时发出终端驱动程序发送此信号并送到前台程序中的每一个进程终止SIGQUIT该信号和SIGINT类似但由QUIT字符通常是Ctrl\来控制终止SIGKILL该信号用来立即结東程序的运行,并且不能被阻塞、处理和忽略。终止SIGALRM该信号当一个定时器到时的时候发出。终止SIGSTOP该信号用于暂停一个进程,且不能被阻塞、处理或忽略。暂停进程SIGTSTP该信号用于暂停交互进程,用户可键入SUSP字符(通常是CtrlZ)发出这个信号。暂停进程SIGCHLD子进程改变状态时,父进程会收到这个信号忽略SIGABORT该信号用于结束进程终止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 用户名(用户所有进程)六.运行新进程在UNIX体系中,创建进程与加载新的二进制镜像分离exec系统调用一次系统调用会把二进制程序加载到内存中,替换地址空间原先内容,并开始执行。这个过程称为“执行”fork()函数另一个不同的系统调用是用于创建一个新进程,他相当于是复制其父进程练习-多进程实现文件分块拷贝// 多进程实现文件分块拷贝 // 父进程拷贝源文件的前半部分 // 子进程拷贝源文件的后半部分 #include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h // 函数获取源文件的大小 int get_srcfile_size(const char *srcfile) { // 以只读的方式打开源文件 int fd 0; if (-1 (fd open(srcfile, O_RDONLY))) { perror(打开源文件失败); return -1; } printf(打开源文件成功\n); // 获取源文件的大小 lseek off_t s 0; if (-1 (s lseek(fd, 0, SEEK_END))) { perror(获取源文件大小失败\n); return -1; } printf(获取源文件大小成功,s %ld\n, s); // 关闭文件 close(fd); // 将源文件的大小进行返回 return s; } // 函数父子进程进行文件拷贝 /*参数说明 srcfile源文件的路径 destfile目标文件的路径 offset拷贝的起始位置 size拷贝的大小 */ int file_copy(const char *srcfile, const char *destfile, off_t offset, off_t size_len) { // 1.以只读的方式打开源文件 int src_fd 0; if (-1 (src_fd open(srcfile, O_RDONLY))) { perror(打开源文件失败); return -1; } printf(打开源文件成功,src_fd%d\n, src_fd); // 2.以只写的方式打开目标文件 // 如果目标文件不存在则创建新的文件 // 如果目标文件存在则将原有的内容进行清空 int dest_fd 0; if (-1 (dest_fd open(destfile, O_WRONLY | O_CREAT | O_TRUNC, 0664))) { perror(打开目标文件失败); return -1; } printf(打开目标文件成功,dest_fd%d\n, dest_fd); // 3.修改光标的位置(父子进程拷贝的源文件中的内容是不同的) // SEEK_SET是从文件的开头进行偏移 lseek(src_fd, offset, SEEK_SET); // 父进程 lseek(dest_fd, offset, SEEK_SET); // 子进程 // 4.循环拷贝工作 char buf_read[BUFSIZ] {0}; ssize_t read_size 0; // 读取到的字节数 ssize_t size 0; // 统计总的字节数 // 循环从源文件中读取数据,写入到目标文件中 // 注意拷贝读取到的内容要比需要拷贝的内容的字节数要多 // 这个时候就需要进行特殊的处理 while (0 (read_size read(src_fd, buf_read, sizeof(buf_read)))) { // 统计读取到的总字节数 size read_size; // 当size累计超过或者是等于需要拷贝的大小的时候 // 说明本次读取到的内容是需要进行截断的 // 我们只需要取到我们需要的部分就可以了 if (size size_len) { // 源文件大小50个字节 //父25 子25 //每次读取10个字节 //10 10 10 需要从30中截取出来5个字节 //父进程才可以拿到25个字节 //所以第三次操作的时候父进程只需要写5个字节即可 //size-size_len:超过的字节数 //read_size-(size-size_len):剩下要拷贝的字节数 write(dest_fd,buf_read,read_size-(size-size_len)); //跳出循环,拷贝结束 break; } else { // 写入到目标文件中 write(dest_fd, buf_read, read_size); } } // 5.关闭文件 close(src_fd); close(dest_fd); //进行返回 return 0; } int main(int argc, const char *argv[]) { // 1.对输入参数的参数个数进行判断 if (argc ! 3) { printf(输入的参数个数有误\n); printf(usage:./a.out srcfile destfile\n); return -1; } // 2.获取源文件的大小 -- 函数 int srcfile_size get_srcfile_size(argv[1]); // printf(源文件的大小为%d\n,srcfile_size); if (srcfile_size 0) { perror(获取源文件大小失败); return -1; } // 3.父子进程拷贝源文件 -- 函数 // 3.1 使用fork函数创建子进程 // 3.2 父子进程拷贝文件 pid_t pid fork(); if (pid 0) { // 父进程 // 父进程拷贝源文件的前半部分 file_copy(argv[1], argv[2], 0, srcfile_size / 2); } else if (pid 0) { // 子进程 // 子进程拷贝源文件的后半部分 file_copy(argv[1], argv[2], srcfile_size / 2, srcfile_size - srcfile_size / 2); } return 0; }
返回列表