北京市建设规划网站,有哪些html网页设计的比赛,360ssp里的网站建设,有什么好看的网站#x1f525;个人主页#xff1a;Quitecoder
#x1f525;专栏#xff1a;linux笔记仓 目录 01.进程的基本概念进程的组成部分进程的特性进程的状态 02.PCBPCB的组成部分task_structtask_struct 的主要组成部分 03.进程属性查看进程 04.通过系统调用创建进程-fork初识工作…
个人主页Quitecoder
专栏linux笔记仓 目录 01.进程的基本概念进程的组成部分进程的特性进程的状态 02.PCBPCB的组成部分task_structtask_struct 的主要组成部分 03.进程属性查看进程 04.通过系统调用创建进程-fork初识工作原理返回值使用示例用途和重要性fork返回值一次创建多个进程 01.进程的基本概念
在计算机科学中进程是操作系统中的一个基本概念代表了计算机程序的一次执行实例。进程不仅包括正在执行的程序代码还包括程序的当前活动包括程序计数器的当前位置、处理器的寄存器和变量的值。简而言之进程是一个具有自己独立功能的程序在某个数据集上的运行过程它可以分配和管理资源。
进程的组成部分
程序代码文本段这是静态的程序指令是进程执行的实际代码。当前活动 程序计数器指示接下来要执行的指令位置。寄存器集存储指令执行前后的中间值。 堆栈栈段用于存储执行期间的局部变量、函数参数、返回地址等。数据段包括全局变量和静态变量它们在程序启动时被初始化程序结束时销毁。堆用于动态内存分配即程序运行时根据需要进行分配和释放的内存。
进程的特性
动态性进程是程序的一次执行过程它有创建、运行、等待、终止等多种状态。并发性多个进程可以在一个或多个处理器上并发执行。独立性进程是资源分配和调度的独立单位具有独立的地址空间和系统资源。结构性进程可以拥有子进程形成进程的层次结构。
进程与线程的区别 虽然进程和线程都是独立调度的执行单位但进程拥有完全独立的地址空间而线程则是进程内的一个相对独立的、可调度的执行单元与同属一个进程的其他线程共享地址空间和资源。 进程的状态
进程在其生命周期中可能会经历几种状态
新建状态进程正在被创建。就绪状态进程已准备好运行等待CPU时间。运行状态进程正在CPU上执行。等待/阻塞状态进程等待某个事件发生如输入/输出操作完成。终止状态进程完成执行或被终止。
02.PCB
在操作系统中进程控制块Process Control Block, PCB 是一个关键的数据结构它用来存储和管理与特定进程相关的所有信息。每当操作系统创建一个新的进程时都会为这个进程分配一个PCB作为管理该进程的核心。PCB是操作系统内核用于进程调度和管理的主要工具。
PCB的组成部分
PCB通常包括以下几个关键的组成部分以便于完整描述一个进程的状态和属性 进程标识符Process Identifier, PID 每个进程都有一个唯一的标识符PID用于在系统中唯一地标识该进程。 处理器状态信息 包括程序计数器PC指明当前正在执行或下一个要执行的指令地址。包括CPU寄存器的内容如通用寄存器、累加器等它们需要在进程切换时被保存和恢复。 进程调度信息 包括进程优先级、进程调度状态如就绪、运行、等待等以及其他与调度相关的信息。处理器时间片时间片调度情况下和其他调度参数。 内存管理信息 包括指向进程地址空间的信息如基址寄存器、边界寄存器、页表信息或段表信息。描述进程内存使用情况如用户栈指针等。 会计信息Accounting Information 包括CPU使用时间、进程创建时间、用户时间和系统时间等统计信息。可能还包括系统资源使用情况如I/O操作次数、使用内存页的数量等。 I/O状态信息 包括与进程相关的I/O设备、I/O请求列表和状态。进程使用的I/O文件相关信息如文件描述符表。 进程上下文信息 用于保存进程在切换时的上下文以便后来可以恢复进程的状态。包含中断处理代码所需的信息。
PCB的重要性
进程调度PCB是操作系统进行进程调度的关键依据。调度程序根据PCB中的信息决定哪个进程将获得CPU执行时间。状态切换在多任务操作系统中CPU在不同进程之间切换时需要保存和恢复进程的状态这由PCB完成。资源管理通过PCB操作系统可以跟踪和管理进程所使用的所有资源从内存到I/O设备。
PCB是操作系统核心的一部分它把进程的执行信息与调度、资源管理密切结合。在进程间切换时操作系统通过保存和恢复PCB中的信息使不同进程能够即时停下和继续执行从而保证系统的多任务操作能力。 简单总结进程PCB代码和数据操作系统对于系统的管理本质是对PCB的管理
task_struct
在Linux操作系统中task_struct 是一种实现进程控制块PCB的数据结构它详细记录了进程的所有信息。作为 Linux 内核中最关键的结构之一task_struct 用于进程管理和调度它存储了与进程状态、数据、调度、地址空间、打开的文件等相关的所有信息。
task_struct 的主要组成部分 进程状态 state表示进程的状态如可运行、不可运行阻塞或停止。 调度信息 priority 和 static_prio动态优先级和静态优先级用于调度决策。sched_entity包含用于 CFS 调度器完全公平调度器的运行时统计信息。 内存管理 mm指向 mm_struct 的指针该结构包含虚拟内存区域和页表等信息。 进程标识符 pid进程的唯一标识符。tgid线程组ID用于标识线程组即与主进程共享同一地址空间的所有线程中的所有线程。 进程关系 parent、children、sibling指向父进程、子进程列表和兄弟进程的指针。 进程执行上下文 thread包含执行时所需的寄存器、栈指针、程序计数器等信息。 文件系统 files指向打开的文件描述符数组的指针。fs指向文件系统特定信息的指针。 信号处理 signal管理信号状态如挂起的信号、信号处理函数等。blocked、real_blocked、saved_sigmask与信号屏蔽相关的字段。 时间信息 start_time进程开始的时间。utime 和 stime用户态和核心态消耗的 CPU 时间。 链接列表 tasks使用 Linux 的双向链表机制将所有 task_struct 连接在一起。
task_struct 的重要性
进程管理和调度Linux 使用 task_struct 管理所有与进程相关的数据。调度器使用这些数据来做出调度决策实现有效的进程调度和管理。系统资源管理task_struct 记录了进程使用的所有系统资源包括文件、内存和信号。这有助于操作系统有效地管理资源和实施安全策略。多任务处理task_struct 允许 Linux 操作系统实现真正的多任务处理能力通过时间共享机制允许多个进程并发执行。
在 Linux 内核中task_struct 结构由于其包含了操作系统管理进程所需的几乎所有信息而成为非常重要的核心部分。内核开发者和系统程序员经常需要直接或间接地与 task_struct 交互以实现高级功能或调试。 03.进程属性
启动 ./XXX本质就是让系统创建进程并运行—我们自己写的代码形成的可执行系统命令可执行文件。在linux中运行的大部分执行操作本质都是运行进程!!!每—个进程都要有自己的唯—标识符叫做进程pid一个进程想知道自己的pid 构建一个无限循环的文件让其进程一直运行
ps ajx | head -1 ps ajx | grep mytest首先打印出进程列表的列标题。然后搜索和显示所有包含mytest的进程条目 查看进程 通过系统调用获取进程标示符操作系统提供自己不能直接调用pid
进程idPID父进程idPPID
#include stdio.h
#include sys/types.h
#include unistd.h
int main()
{printf(pid: %d\n, getpid());printf(ppid: %d\n, getppid());return 0;
}验证 ctrl C.就是在用户层面终止进程kill -9 pid可以用来直接杀掉进程 进程每次启动的pid不一样但是父进程是不变的 这里的 -bash 表示的是一个 Bash shell 的实例是一个命令行界面用户可以在其中输入和执行命令。
04.通过系统调用创建进程-fork初识 在 Unix 和类 Unix 系统如 Linux中fork() 是一种系统调用用于创建一个新的进程这个新的进程被称为子进程。这个子进程是调用它的父进程的一个副本。fork() 是进程复制和进程创建最基本的方式它在很多类型的软件应用中都非常重要尤其是在操作系统和并发计算领域。
工作原理
当程序调用 fork() 时操作系统会创建一个与调用它的进程称为父进程几乎完全相同的新进程。这个新创建的子进程将会
复制父进程的地址空间包括代码段、数据段、堆和栈。这意味着父进程和子进程的变量和程序的运行环境在物理内存中是分开的但初始值相同。继承父进程的文件描述符。如果父进程打开了文件则子进程也将拥有这些文件的打开副本共享同样的文件位置指针。继承父进程的环境设置和任何其它相关的上下文信息。
返回值
fork() 函数调用后会有两次返回
在父进程中fork() 返回新创建的子进程的进程 ID。在子进程中fork() 返回 0。
如果出现错误如内存不足fork() 只会在父进程中返回一个负值并且不会创建子进程。
使用示例
#include unistd.h
#include stdio.hint main() {int pid fork();if (pid 0) {// 这是子进程printf(This is the child process. PID %d\n, getpid());} else if (pid 0) {// 这是父进程printf(This is the parent process. PID %d, Child PID %d\n, getpid(), pid);} else {// fork失败perror(fork failed);}return 0;
}用途和重要性
并发fork() 允许程序并发运行多个进程。这是创建多进程应用程序的基础如 Web 服务器和网络服务。资源共享通过 fork() 创建的进程可以共享某些资源如文件描述符这可以用于进程间通信。程序复杂性管理通过将任务分配给子进程可以简化复杂应用程序的设计使得代码更容易理解和维护。安全性和隔离在某些应用中fork() 可以用来隔离不同的任务例如在安全关键的应用中隔离不信任的代码。
下面我们来看
while :; do ps ajx |head -1 ps axj |grep mytest |grep -v grep ; sleep 1; done每隔一秒打印一次进程 刚开始只有一个进程在跑后面两个进程跑最后退出
fork之后父子代码共享创建一个进程本质是系统中多了一个进程多了1.内核task_struct 2.有自己的代码和数据父进程的代码和数据是从磁盘加载来的子进程的代码和数据默认情况继承父进程的代码和数据 执行的两个打印流一个是父进程一个是子进程
我们创建子进程的目的是为了让子进程和父进程执行不一样的代码上面的代码执行相同没有太大意义
fork返回值
上面提到fork() 函数调用后会有两次返回
在父进程中fork() 返回新创建的子进程的进程 ID。在子进程中fork() 返回 0。
如果出现错误如内存不足fork() 只会在父进程中返回一个负值并且不会创建子进程。 使用 fork() 系统调用来创建子进程并分别在父子进程中实现了无限循环打印各自的状态信息。父子进程的行为及如何使用 fork()。下面是该程序的逐行解析及行为说明
#include stdio.h
#include unistd.h
#include sys/types.hint main() {// 输出当前进程的状态表明只有这一个进程在运行printf(process is running, only me! pid:%d\n, getpid());sleep(3); // 让进程暂停3秒pid_t id fork(); // 创建子进程if (id -1)return 1; // 如果fork失败程序返回1并退出else if (id 0) {// 子进程执行的代码块while (1) {// 持续打印子进程的信息子进程中fork()返回0printf(id:%d, I am child process, pid: %d, ppid: %d\n, id, getpid(), getppid());sleep(1); // 每隔1秒重复打印}}else {// 父进程执行的代码块while (1) {// 持续打印父进程的信息在父进程中fork()返回子进程的PIDprintf(id:%d, I am parent process, pid: %d, ppid: %d\n, id, getpid(), getppid());sleep(2); // 每隔2秒重复打印}}return 0;
}程序开始 打印初始进程信息只有主进程在运行。getpid() 返回当前进程的PID。sleep(3) 暂停3秒以便观察初始状态。 进程分叉fork 调用 fork() 创建一个新的子进程。在父进程中fork() 返回子进程的PID。在子进程中fork() 返回0。 错误处理 如果 fork() 返回-1表示创建子进程失败程序返回1并终止。 子进程行为 子进程进入无限循环每秒打印一次自己的状态信息ID为0当前PID以及父进程的PID。 父进程行为 父进程也进入无限循环每2秒打印一次自己的状态信息包括子进程的PID当前PID以及父进程的PID。 我们以前写过的代码全都是单进程
这里会有疑问
同一个id怎么可能即是0又是0这里和后面要学的虚拟地址空间和父子写实拷贝有关系fork函数为什么会返回两次
fork() 函数之所以会返回两次是因为它在被调用时负责创建一个新的进程子进程。在调用 fork() 时操作系统会通过复制发出 fork() 调用的进程父进程来创建子进程。这一过程生成了两个几乎完全相同的进程原有的父进程和新创建的子进程从而使得 fork() 看似返回了两次但实际上是在两个不同的进程中返回 在父进程中返回对于父进程fork() 返回新创建的子进程的进程IDPID。这使得父进程能够识别和管理其子进程。例如如果所返回的PID是正数n则表明创建成功n即为子进程的PID。 在子进程中返回对于子进程fork() 返回0。这一返回值通常用于让子进程执行不同于父进程的代码。子进程通过区分fork()的返回值来决定其工作逻辑。 错误返回如果 fork() 调用失败例如由于系统资源不足或超过了系统允许的进程数则它只会在父进程中返回 -1并且不会创建子进程。在此情况下通常使用 errno 获取错误的具体信息。
进程具有独立性我们杀掉其中一个不影响另一个父子进程有独立的PCB
一次创建多个进程 代码解析
#include stdio.h
#include unistd.h
#include sys/types.hvoid RunChild()
{// 子进程的无限循环输出while(1){printf(I am child, pid:%d, ppid:%d\n, getpid(), getppid());sleep(1); // 每隔一秒打印一次}
}int main()
{const int sum 5; // 要创建的子进程数量// 在父进程中进行循环创建子进程for(int i 0; i sum; i){pid_t id fork(); // 创建新的子进程if (id 0){RunChild(); // 如果是子进程调用 RunChild 函数}// 父进程会继续循环不会被 RunChild 调用所阻塞}// 父进程的无限循环输出while(1){printf(I am parent, pid:%d, ppid:%d\n, getpid(), getppid());sleep(1); // 每隔一秒打印一次}return 0; // 虽然永远不会到达这里因为有无限循环
}进程创建 主进程通过 for 循环调用 fork() 五次这将原始父进程派生出五个子进程。在每次调用 fork() 后系统生成两条执行路径父进程继续循环子进程跳转到 RunChild()。 子进程行为 一旦 fork() 返回 0这个进程即为子进程它会调用 RunChild() 函数。在 RunChild() 中子进程进入无限循环持续输出其 PID 和其父进程的 PID暂停 1 秒确保不会占用太多 CPU 时间。 父进程行为 原始父进程从不进入 RunChild() 的分支继续 for 循环执行五次 fork()然后进入自己的无限循环。在这个无限循环中父进程以 1 秒的间隔输出其 PID 和父进程 PID。 进程的信息可以通过 /proc 系统文件夹查看 进程中的PCB会记录自己对应的可执行程序的路径