有没有一种app类似网站建设,邻水县规划和建设局 网站,电脑软件推广联盟,自己注册公司需要什么资料什么是进程
想要了解什么是进程#xff0c;或者说#xff0c;为什么会有进程这个概念#xff0c;我们就需要去了解现代计算机的设计框架(冯诺依曼体系)#xff1a;
计算机从设计之初就以执行程序为核心任务#xff0c;也就是运算器从内存中读取#xff0c;也只从内存中…什么是进程
想要了解什么是进程或者说为什么会有进程这个概念我们就需要去了解现代计算机的设计框架(冯·诺依曼体系)
计算机从设计之初就以执行程序为核心任务也就是运算器从内存中读取也只从内存中读取输入设备也只能输入到内存中。在早期计算机系统中计算机一次只能运行一个程序所有资源都被这个程序独占。随着需求的增加计算机需要能够同时运行多个任务比如一个任务处理文件读写另一个任务计算数据。这时系统需要一种机制来管理多个程序的执行确保它们能够公平、有效地利用计算机的资源这就是进程概念产生的原因。
进程的定义
进程是操作系统中执行程序的一个实例它包含了程序运行所需的所有信息和资源。简单来说进程就是操作系统为运行中的程序提供的一个执行环境包括程序代码、数据、打开的文件、内存空间和处理器时间等。一个程序在操作系统中可能运行多个实例每个实例就是一个独立的进程。
每个进程通常包含以下几个部分
可执行程序代码程序的二进制指令。进程的内存空间包括堆、栈、数据段等。进程状态信息CPU寄存器、程序计数器等记录当前进程执行的具体状态。调度信息操作系统需要的信息用来决定何时执行该进程。
这些也就是计算机或者说是操作系统用来描述每一个需要执行的程序的方法在linux里描述程序的结构体叫做 task_struct 也叫做PCB Process Control Block,进程控制块PCB是一个概念task_struct则是在linux里PCB的具体表现。
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, 0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;int lock_depth; /* Lock depth */int prio, static_prio;struct list_head run_list;prio_array_t *array;
}
这是在 linux-2.6.11.1 截取的task_struct 片段通过这个片段我们可以看到操作系统用来描述进程信息的部分属性。
state 表示进程的当前状态可运行、不可运行等。usage 是引用计数记录进程被使用的次数。prio 和 static_prio 是进程的优先级用于调度。run_list 用于将进程挂入运行队列array 管理进程的优先级队列。
struct list_head {struct list_head *next, *prev;
}; 而这里链接进程进入运行队列的结构就是这个结构体
通过上篇博客不同结构体之间的链接-CSDN博客的字节运算从而获得每一个结构体的成员。
为什么需要进程
引入进程的核心原因在于多任务处理和资源管理。现代计算机需要同时运行多个任务而进程是管理这些任务的一种机制。进程可以
隔离任务每个进程运行在自己的内存空间中避免相互影响。这种隔离性提高了系统的安全性和稳定性。资源管理操作系统通过进程来分配 CPU、内存和I/O设备等资源确保每个进程都能公平地使用系统资源。并发执行通过多任务调度操作系统可以在一个 CPU 上快速切换进程使得多个进程看似同时执行提升系统的利用效率。
进程的出现使得操作系统能够高效管理多个任务并为每个任务提供独立、受保护的执行环境。
查看进程
在window操作系统中由于图形化的原因我们可以通过任务管理器更加方便直接的看到当前计算机正在运行的进程同时我们还可以看到他的基本信息那么在Linux系统中我们该如何查看进程呢
我们可以直接在 /proc/ 文件夹里查看进程 这些蓝色的数字就是进程的PID 唯一标识符 如果我们想要查进程信息同样可以进入文件夹内查看 大多数进程信息同样可以使用top和ps这些用户级工具来获取
我们可以写一个一直循环的进程用来使用ps查询
#include stdio.h
#include unistd.hint main()
{while (1){sleep(5); // 暂停 5 秒printf(mypid: %d\n); }return 0;
}为什么有两个呢因为作为查询任务的 grep 也是一个进程 同样我们也可以使用系统接口在程序内获取进程PID 和父进程的PID PPID 每一次我们执行程序进程PID都是改变的
top命令大多数情况下都是用在进程优先级上我们在优先级那里在回顾top的使用 创建进程
在系统给出的系统调用接口中也有创建子进程的相关函数那就是 fork() 。 fork() 函数是 Unix 和 Linux 系统中用于创建新进程的系统调用。它是进程控制的一部分用于实现多任务处理。fork() 在调用时会创建一个新的进程这个新进程是调用进程的一个副本称为子进程
创建新进程fork() 会复制调用进程的所有属性创建一个新的进程。返回值 在父进程中fork() 返回新创建的子进程的 PID。在子进程中fork() 返回 0。如果 fork() 失败它会返回 -1并设置 errno 以指示错误原因。 我们通常使用 if 来分流父进程与子进程
#includestdio.h
#includeunistd.h int main()
{ pid_t id fork(); if(id0) //子进程 { int i 5; while(i--) { printf(我是子进程 pid : %d, ppid : %d\n,getpid(),getppid()); sleep(1); } } else if(id0) { int i7; while(i--) { printf(我是父进程 pid: %d,ppid: %d\n,getpid(),getpid()); sleep(1); } } else { perror(进程创建失败\n); } return 0;
} fork函数 上述代码我们可以看出来我们创建了一个子进程但是在以往我们学习C语言的经验里一个程序里一个值怎么进入两个完全不同的if判断呢那我们最直观的想法就是父进程跟子进程使用的数据并不是用一个甚至并没有在一起。
在fork官访问当中我们可以看到 内存空间的独立性 进程复制 当调用 fork() 时操作系统会创建一个新的进程子进程这个子进程是父进程的一个副本。在内存中这意味着子进程会有一份与父进程相同的内存内容包括代码、数据、堆栈等。 独立的内存空间 虽然父进程和子进程在 fork() 时内存内容是相同的但它们的内存空间是独立的。每个进程在其自己的地址空间中操作因此一个进程的内存更改不会直接影响到另一个进程的内存。 写时复制Copy-on-Write 在现代操作系统中fork() 采用了一种优化机制叫做写时复制Copy-on-Write。最初父进程和子进程共享相同的物理内存页只有当其中一个进程试图修改这些内存页时操作系统才会复制这些页确保每个进程拥有自己的副本。这种机制减少了 fork() 操作的开销。 子进程与父进程的分流
当我们了解 fork()函数后了解了fork()函数之后的代码是子进程与父进程共有的当子进程要修改某一个变量的值时系统才会给子进程拷贝一份这个值并开辟空间给其进行修改那我们是不是可以通过取地址 id 的地址来获得子进程存放数据的地址呢
#includestdio.h
#includeunistd.h int main()
{ pid_t id fork(); if(id0) //子进程 { printf(%p\n,id); } else if(id0) { printf(%p\n,id); } else { perror(进程创建失败\n); } return 0;
} 我们惊奇的发现两个id的地址竟然是一样的那我们之前的拷贝的id在哪里同一个地址给出的id值竟然会不一样 其实这是操作系统包含内存的设计如果让我们的程序简简单单就可以访问到物理地址那么操作系统很容易就崩溃了。对与每一个进程或者说是每一个程序操作系统都平等分配一个虚拟地址空间通过这个虚拟地址空间跟页表映射到真实的物理地址那么也就是说这两个id不同的是物理地址。 当fork函数返回时对id进行了写时拷贝子程序的页表重新映射到操作系统存放的新id的物理地址简图 当子进程被创建时PCB等内容被填充父子进程指向相同的代码同时父子进程都有了属于自己的task_struct可以被操作系统调度并执行了同时由于直接拷贝导致消耗过大也就有了修改数据时分配空间同时让子进程的页表指向该空间从而获得不同的数据。页表的设计将虚拟地址跟物理地址连接到一起并且让内存单独维护内存进程单独维护进程保护数据的同时解耦两部分。
ps: Linux 源码下载 Index of /pub/linux/kernel/