一、进程管理
一、进程
1、什么是进程?
进程就是运行中的程序
1、用户角度: 进程是程序的一次动态执行过程
2、操作系统: 进程是操作系统分配资源的基本单位,也是最小单位
一个简单的c程序,要让它执行,先要经过汇编汇编,再通过编译器编译,再通过链接器形成可执行文件,再通过装载器加载到内存中执行。这种可执行文件交给系统执行就成为进程了。
操作系统有一种可执行文件格式, 这个格式是一种数据结构。
2、程序进程的区别
3、进程状态的转换
二、进程控制
1、进程控制块PCB
PCB是操作系统用来管理进程的数据结构,用它来记录进程的外部特征,描述进程的运动变化过程。
PCB的结构就是task_struct结构(头文件sched.h)
每个进程都有一个非负的唯一进程ID(PID)。虽然是唯一的,但是PID可以重用,当一个进程终止后,其他进程就可以使用它的PID了。
PID为0的进程为调度进程,该进程是内核的一部分,也称为系统进程;PID为1的进程为init进程,它是一个普通的用户进程,但是以超级用户特权运行;PID为2的进程是页守护进程,负责支持虚拟存储系统的分页操作。
每个进程的task_struct和系统空间堆栈存放位置如下:两个连续的物理页
2、进程创建
新进程的创建,首先在内存中为新进程创建一个task_struct结构,然后将父进程的task_struct内容复制其中,再修改部分数据。分配新的内核堆栈、新的PID、再将task_struct 这个node添加到链表中。所谓创建,实际上是“复制”。
子进程刚开始,内核并没有为它分配物理内存,而是以只读的方式共享父进程内存,只有当子进程写时,才复制。即“copy-on-write”。
利用fork函数创建进程:
fork
函数用于创建一个与当前进程映像一样的子进程,所创建的子进程将复制父进程的代码段、数据段、BSS段、堆、栈等所有用户空间信息,在内核中操作系统会重新为其申请一个子进程执行的位置。
fork都是由do_fork实现的,do_fork的简化流程如下图:
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int cnt = 1;
int main(void)
{
//派生进程
pid_t pid = fork();
//派生失败
if(pid < 0)
{
perror("fork error");
exit(1);
}
//派生成功返回值为0表示子进程
else if(pid == 0)
{
printf("\nchild: forkval=%d pid=%d cnt=%d\n", pid, getpid(), ++cnt);
sleep(3);//由于父子进程是并发的
}
//派生成功返回值大于0表示父进程,父进程中将返回子进程的PID。
else if(pid > 0)
{
printf("\nparent: forkval=%d pid=%d cnt=%d\n", pid, getpid(), cnt);
while(1);//由于父子进程是并发的
}
return 0;
}
进程创建过程: