原文地址:http://blog.chinaunix.net/u/12783/showart_722257.html
前面讲到了进程的上下文以及如何管理/操作进程的地址空间,其中,里面提到了一些调用。这些调用与下文有一定的关联,在此列出:
fork():调用dupreg(),attachreg()
exec():调用detachreg(),allocreg(),attachreg(),growreg(),loadreg(),mapreg()
brk():调用growreg()
eixt():调用detachreg()
当然还有其它的一些调用与进程控制有关,如signal(),wait(),kill()等,以后会讲到。
创建进程
一个进程的生命起点是fork(),由其父进程调用以产生该进程。fork()是UNIX系统上唯一一个能够创建新进程的调用,当然,除了init进程,这个进程是由内核在系统boot时创建的,其PID是0。系统启动完成后,init进程将推出,取而代之的是PID为1的swap进程(有时候又叫做idle进程)。
在执行fork()时,kernel将会做一些列的操作:
1、在进程表中为新进程申请一个表项;
2、为新进程分配一个唯一的PID;
3、将父进程的上下文及地址空间做一个逻辑拷贝——
一般来说,只需要拷贝数据段和堆栈段等,文本段无需拷贝,因为父子进程共享该段。若子进程立即调用exec()家族的函数,则更不需要拷贝文本段,数据段和堆栈段也无需拷贝,因为将会有一个全新的image加载到子进程并运行;
4、因为父子进程共享文件,所以增加文件和inode的引用计数;
5、将子进程的状态设置为Read to Run;
6、返回两次:将子进程PID返回给父进程(可用于跟踪子进程状态),将0返回给子进程;
关于fork()的实现,请求分页和交换系统有所不同,本文将讨论交换系统上的实现,并假定有足够的内存来创建子进程。以后讨论分页系统的实现。下面是伪代码:
PID fork()
{
Assert(EnoughMemory && EnoughResources);
PID aPid = GetFreeProcTableEntry(); // 同时得到进程表项。
Assert(NumOfProcs(GetCurrentUser()) < MAX_PROC_NUM_PER_USER); // 保证一个用户不会创建太多进程。
procTable[aPid]->State = CREATED;
CopyData(procTable[aPid], procTable[getppid()]); // 从父进程的表项中拷贝数据,如有效uid,当前目录等等。
/* 增加当前目录及改变的根的inode引用计数;
增加文件表中打开文件的引用计数;
拷贝一份父进程的上下文(包括u area,文本段、堆栈段、数据段);
将一个假系统级上下文层压到子进程的系统上下文栈,该上下文层包括能够让子进程识别自己的一些信息,并且当子进程获得处理器时从此处开始执行; // 就像进程被调度时保存的上下文。*/
if(IsParent()) // 一旦被调度,将从假系统级上下文层开始执行,父子进程将共享下面的代码。
{
procTable[aPid]->State = READY_TO_RUN; // 此时子进程才可能被调度。
return aPid;
}
else
{
初始化u area的与分时相关的一些字段;
return 0;
}
}
fork()的实现是相当复杂的,但是,抓住三点就好理解了:上下文、调度、共享资源。在将子进程放入调度队列之前,先设置子进程的上下文信息(包含那个假系统级上下文层)。当子进程被调度,它将执行从“if (IsParent())”开始的那段代码。同样,父进程在设置子进程的上下文信息后也执行这段代码,看起来好像父子进程都是在该点“被调度”,从运行态转为其他状态。
在交换系统上,创建进程时需要两份空间来存放子进程:一份在内存,一份在磁盘——因为子进程的状态为READ_TO_RUN,如果此时系统没用可用内存,而它又处于低优先级,它就有可能被交换到磁盘上。另外需要注意的是,在获取空闲的进程表项时需要有一定的锁机制来避免竞态条件。
获取了进程表项之后,子进程便处于“被创建”(或“正被创建”)状态。此时,内核将会让这个新生儿“继承”其父进程的进程表项,并将父进程的pid设置到子进程,以便子进程识别父进程。同时,子进程将被放到进程的树结构中——同时还会设置其优先级、初始化调度参数等等。
之后便是调整一些引用计数。关于打开的文件的引用计数,大家可以用经典的“协同进程”方法做实验。
还记得u area吗?只有通过u area,内核才能识别一个运行中的进程。它也是进程上下文中静态系统级上下文的一员。然而,在fork()时,除了其中进程表项的指针,父子进程的u area完全一样。当然,在fork()执行完之后,这两个u area就可能不一样了。
好了,进程所需的所有的静态上下文都已经创建好了,内核需要做的就是为子进程populate一个假系统上下文层。大家要注意了!内核将父进程的第一层上下文拷贝给子进程,该层包括用户保存的寄存器上下文和调用fork()的内核栈楨,然后创建一个假的系统上下文层,该层保存了上一层以及其他的寄存器信息,设置正确的PC(“if (IsParent())”),以保证子进程能够被正确的“恢复执行”——虽然子进程从来没有执行过。
分享到:
相关推荐
2.1.2用户的注册与注销11 2.1.3账户的管理12 2.1.4用户口令的管理12 2.1.5用户组信息13 2.2初识UNIX的shell13 .2.2.1什么是shell程序13 2.2.2shell的内部命令和外部命令14 2.3UNIX系统启动及用户登录过程14 2.3.1...
#### 一、初识Unix:从Windows开始 Unix作为操作系统领域的一位元老,其影响力深远,不仅为现代操作系统的发展奠定了基础,还孕育了诸如Linux这样的开源巨擘。对于初次接触Unix的新手而言,学习旅程可以从熟悉的...
除了基本的文件操作,UNIX还提供了许多其他工具,如进程管理(ps、kill、killall)、网络通信(netstat、ping、telnet)和文本处理(sed、awk)。学习这些命令能让你更有效地在UNIX环境中工作。 此外,UNIX的shell...
在提供的压缩文件中,`shell编程和unix命令1-9.rar`涵盖了初识阶段,可能包含前10章的基础知识;`shell编程和unix命令10-20.rar`可能进一步讲解了更复杂的Unix命令和Shell编程技巧;而`shell编程和unix命令21-30.rar...
初识计算机网络,我们需要了解其构成、功能以及相关的网络协议。 首先,计算机网络的构成主要包括以下几个部分: 1. 网络服务器:作为网络的核心,服务器运行着局域网的操作系统,管理网络资源,处理通信,响应...
1. Windows进程初识:学生将学习如何使用VC++编写Win32 Console Application,掌握Windows API的使用,以及测试用户态和核心态运行程序的方法。 - 编写基础的Win32 Console Application,创建新的工程,编写源代码...
【Linux入门】初识Linux与UNIX的区别 在深入学习Linux之前,我们首先需要理解Linux与UNIX之间的联系和差异。Linux的起源可以追溯到1991年,由芬兰大学生Linus Torvalds开发,最初是一个个人项目,目的是为了提供一...
- **进程管理**:学会使用`ps`(显示进程信息)、`kill`(终止进程)等命令来管理正在运行的进程。 - **文本处理**:熟悉`grep`(搜索文本)、`sed`(文本流编辑器)、`awk`(强大的文本处理工具)等命令可以帮助...
A Process Control System 使用b/s架构、运行在类Unix系统上一个进程监控管理系统它可以使进程以daemon方式运行,并且一直监控进程,在意外退出时能自动重启进程。 安装 Supervisor是使用python开发的一个进程管工具...
System V IPC是Unix和类Unix系统中进程间通信的一种方式,包括信号量、消息队列和共享内存。这些机制允许进程间交换信息,协同工作。 "进程和线程"章节将介绍Linux内核如何管理和调度进程,以及线程的实现。这包括...
- Linux操作系统基于POSIX和UNIX标准,广泛应用于服务器、移动设备和个人电脑。 - Ubuntu是基于Debian的发行版之一,以其用户友好性和强大的社区支持而闻名。 - **Ubuntu特性:** - 软件包管理系统(APT):易于...
nginx在启动后,在unix系统中会以daemon的方式在后台运行,后台进程包含一个master进程和多个worker进程。我们也可以手动地关掉后台模式,让nginx在前台运行,并且通过配置让nginx取消master进程,从而可以使nginx以...
本书首先会介绍Linux的基础知识,包括它的历史、发行版以及与Unix的关系,帮助读者建立起对Linux环境的整体认知。 接下来,书中会详细讲解如何在Linux环境下设置开发环境,包括安装必要的编译工具、编辑器(如vim或...