操作系统7----进程控制

本文详细介绍了进程控制的关键概念和技术,包括进程切换、创建、加载和执行、等待和终止等。通过具体的系统调用如fork、exec、wait和exit,阐述了进程生命周期中的重要阶段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                               进程控制

1.进程切换

2.进程创建fork

3.进程加载和执行exec

4.进程等待wait和终止exit


1.进程切换

进程切换(上下文切换)

暂停当前运行进程,从运行状态变成其他状态,调度另一个进程从就绪状态变成运行状态

进程切换的要求

切换前,保存进程上下文;切换后,恢复进程上下文;快速切换

进程生命周期的信息

寄存器 (PC, SP, …);CPU状态;内存地址空间

进程控制块PCB:内核的进程状态记录

内核为每个进程维护了对应的进程控制块(PCB),内核将相同状态的进程的PCB放置在同一队列

主要有就绪队列,IO等待队列,僵尸队列


2.进程创建fork

创建新进程

Windows进程创建API: CreateProcess(filename)

创建时关闭所有在子进程里的文件描述符                 CreateProcess(filename, CLOSE_FD)

创建时改变子进程的环境            CreateProcess(filename, CLOSE_FD, new_envp)

https://blog.youkuaiyun.com/qq_34490018/article/details/80517329

Unix进程创建系统调用: fork/exec

fork()把一个进程复制成二个进程   parent (old PID), child (new PID)

exec()用新程序来重写当前进程  PID没有改变

https://blog.youkuaiyun.com/c1204611687/article/details/85613492

fork()系统调用

int pid = fork();		// 创建子进程
if(pid == 0) {			// 子进程在这里继续
     // Do anything (unmap memory, close net connections…)
	exec(“program”, argc, argv0, argv1, …);
}

fork() 创建一个继承的子进程,复制父进程的所有变量和内存,复制父进程的所有CPU寄存器(有一个寄存器例外)。

fork()的返回值,子进程的fork()返回0,父进程的fork()返回子进程标识符,fork() 返回值可方便后续使用,子进程可使用getpid()获取PID

fork()的地址空间复制

fork()执行过程对于子进程而言,是在调用时间对父进程地址空间的一次复制,对于父进程fork() 返回child PID, 对于子进程返回值为0。


3.进程加载和执行exec

系统调用exec( )加载新程序取代当前运行进程

允许进程“加载”一个完全不同的程序,并从main开始执行(即_start)

允许进程加载时指定启动参数(argc, argv)

exec调用成功时,它是相同的进程,但是运行了不同的程序

代码段、堆栈和堆(heap)等完全重写

执行fork系统调用,pid返回值小于0,说明fork失败;pid=0,说明为子进程;pid>0,说明为父进程;

在子进程中执行exec调用,可以实现加载新程序来执行

main()
…
int pid = fork();			// 创建子进程
if (pid == 0) {			// 子进程在这里继续
    exec_status = exec(“calc”, argc, argv0, argv1, …);
    printf(“Why would I execute?”);
}  else {				// 父进程在这里继续
    printf(“Whose your daddy?”);
    …
    child_status = wait(pid);
}
if (pid < 0) { /* error occurred */

shell中调用fork()后加载计算器的图示

shell中调用fork()后加载计算器的图示

执行fork调用,内核中持有两个PCB控制块,子进程是对于父进程的拷贝;

当加载exec时,子进程代码段,数据段,堆栈段被覆写,来执行新的程序代码

int  main()
{
     pid_t  pid;
      int  i;

      for  (i=0;  i<LOOP;  i++)
      {
           /* fork  another  process  */
           pid = fork();
           if  (pid < 0) { /*error  occurred  */
                fprintf(stderr, “Fork Failed”);
                exit(-1);
           }
           else if (pid == 0) { /* child process */
                fprintf(stdout,  “i=%d,  pid=%d,  parent  pid=%d\n”,I,      
                             getpid() ,getppid());
           }   
      }
      wait(NULL);
      exit(0);
} 

fork()的开销

fork()的实现开销

对子进程分配内存,复制父进程的内存和CPU寄存器到子进程里

在99%的情况里,在调用fork()之后调用exec(),在fork()操作中内存复制是没有作用的,子进程将可能关闭打开的文件和连接

vfork()  创建进程时,不再创建一个同样的内存映像,创建进程时,不再创建一个同样的内存映像,子进程应该几乎立即调用exec()

使用 Copy on Write  (COW) 技术


4.进程等待wait和终止exit

父进程等待子进程

wait()系统调用用于父进程等待子进程的结束,子进程结束时通过exit()向父进程返回一个值,父进程通过wait()接受并处理返回值

wait()系统调用的功能

有子进程存活时,父进程进入等待状态,等待子进程的返回结果,当某子进程调用exit()时,唤醒父进程,将exit()返回值作为父进程中wait的返回值;

僵尸子进程等待时,wait()立即返回其中一个值;

无子进程存活时,wait()立刻返回;

进程的有序终止 exit()

进程结束执行时调用exit(),完成进程资源回收

exit()系统调用的功能

将调用参数作为进程的“结果”

关闭所有打开的文件等占用资源

释放内存

释放大部分进程相关的内核数据结构

检查是否父进程是存活着的,如存活,保留结果的值直到父进程需要它,进入僵尸(zombie/defunct)状态;如果没有,它释放所有的数据结构,进程结果

清理所有等待的僵尸进程

进程终止是最终的垃圾收集(资源回收)

进程僵尸状态的含义:子进程执行exit调用,释放了相关资源,保存了执行结果,此时父进程仍然存活,因此子进程需要等待父进程是否需要使用子进程的结果,此时子进程处于僵尸状态。

参考:清华大学 操作系统  陈渝  http://os.cs.tsinghua.edu.cn/oscourse/OS2015/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值