2022-12-27 11:48阅读: 81评论: 0推荐: 0

《操作系统导论》-1.1-虚拟化-进程概念及API

虚拟化

操作系统的基本抽象——进程。

人们希望同时运行多个程序,但CPU核心往往是屈指可数的。为了使得每个程序都有自己的CPU可用(至少看起来是这样的),系统将CPU虚拟化,让一个进程只运行一个“时间片”,然后切换到其他进程——即时分共享CPU计数

但是很明显进程共享CPU,进程间的切换会有额外的性能消耗

与“时分共享”相对的,磁盘空间就是一个“空分共享”资源

  • 低级机制:实现所需功能的方法或协议,“如何实现?”
  • 高级智能:操作系统内做出某种决定的算法,“更明智的决策”

进程

进程是操作系统为正在运行的程序提供的抽象

包括了但不限于诸如:

内存、寄存器、IO信息(当前打开的文件列表)

创建

程序如何被转化为一个进程?

  1. 首先,要把代码和所有的静态数据从磁盘加载到内存(进程的地址空间)中

    现代操作系统一般惰性加载(分页和交换机制)

  2. 为程序运行时栈、堆分配一些内存(也可能是程序自己申请并初始化的:malloc()

  3. IO相关的初始化操作

    比如UNIX系统中的每个进程默认有三个文件描述符:标准输入、输出和错误

  4. 最后,通过跳转到main()例程,OS将CPU的控制权转移到新创建的进程中,从而程序开始执行

状态

  • 就绪
  • 运行
  • 阻塞

只有“就绪”和“运行”可以相互切换,进程的切换需要额外的数据结构保存其上下文信息(进程列表、PCB)

API

fork()
// 这是关于调用系统API创建新进程的简单例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("hello world (pid:%d)\n", (int)getpid());
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed\n");
exit(1);
}
else if (rc == 0)
{
printf("hello,I am child (pid:%d)\n", (int)getpid());
}
else
{
printf("hello,I am parent of %d (pid:%d)\n", rc, (int)getpid());
}
return 0;
}

注意这个程序要在Linux(Unix)环境下运行,不然<unistd.h>头文件要报错,因为windows下不提供这个

这边运行的输出长这样:

hello world (pid:3125265)
hello,I am parent of 3125266 (pid:3125265)
hello,I am child (pid:3125266)

程序的主要内容就是在主程序中fork()了一个子进程

注意fork()之后两个进程是同时运行的,并不存在绝对的顺序(由CPU调度程序决定)

所以可能是parent语句先输出,也可能child语句先输出

另外,新创建的子进程虽然是和父进程完全一样的,但它并不从main入口开始执行,而是从fork()位置开始,这也是为什么没有再输出一遍hello world的原因

还有就是,父进程和子进程获取到返回值是不一样的,父进程获取到的是子进程的PID,而子进程获取到的fork()返回值是0,这也是为什么两个进程运行了不同的分支的原因

wait()

父进程需要等待子进程完成的情况

// 这是关于调用系统API让父进程等待子进程执行完毕的简单例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
printf("hello world (pid:%d)\n", (int)getpid());
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed\n");
exit(1);
}
else if (rc == 0)
{
printf("hello,I am child (pid:%d)\n", (int)getpid());
}
else
{
int wc = wait(NULL);// 这里等待子进程执行完成
printf("hello,I am parent of %d (wc:%d) (pid:%d)\n", rc, wc, (int)getpid());
}
return 0;
}
# 程序输出
hello world (pid:3129576)
hello,I am child (pid:3129577)
hello,I am parent of 3129577 (wc:3129577) (pid:3129576)
exec()

fork()只能运行与父进程相同的拷贝进程,而exec()用来运行不同的进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
printf("hello world (pid:%d)\n", (int)getpid());
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed\n");
exit(1);
}
else if (rc == 0)
{
printf("hello,I am child (pid:%d)\n", (int)getpid());
char *myargs[3];
myargs[0] = strdup("wc");
myargs[1] = strdup("testExec.c");
myargs[2] = NULL;
execvp(myargs[0], myargs);
printf("this shouldn't print out");
}
else
{
int wc = wait(NULL);
printf("hello,I am parent of %d (wc:%d) (pid:%d)\n", rc, wc, (int)getpid());
}
return 0;
}
# 程序输出
hello world (pid:3131031)
hello,I am child (pid:3131032)
33 80 730 testExec.c
hello,I am parent of 3131032 (wc:3131032) (pid:3131031)

让我们看看发生了什么:

首先主程序运行,然后创建了一个子进程并等待子进程执行完毕

在子进程中调用了execvp()来运行字符计数程序wc(基于本文件,打印出有多少行、多少单词、字节)

exec()有诸多变体

很奇怪,exec()并没有创建新进程,而是直接将当前运行的程序替换为不同的运行程序

“对exec()的成功调用永远不会返回”?什么意思

为什么这么设计?把fork()exec()分开?

给了shell在fork()之后,exec()之前运行代码的机会,在运行新程序前改变环境,比如:输出重定向,以下是个示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main()
{
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed\n");
exit(1);
}
else if (rc == 0)
{
// 重定向标准输出到指定文件
close(STDOUT_FILENO);
open("./test4.output", O_CREAT | O_WRONLY | O_TRUNC | S_IRWXU);
char *myargs[3];
myargs[0] = strdup("wc");
myargs[1] = strdup("test4.c");
myargs[2] = NULL;
execvp(myargs[0], myargs);
}
else
{
int wc = wait(NULL);
}
return 0;
}
# 输出到test4.output文件中
cat test4.output
32 68 604 test4.c

机制:受限直接执行

前面我们说了,CPU的虚拟化可以通过时间片轮转的而方式,但是这样的做法同样会带来一些挑战:

  1. 首先是进程切换的性能开销,不可避免,但是必须控制在尽量小的范围

  2. 控制权,操作系统不能将CPU等系统资源的控制权完全放出

    防止可能的程序恶意行为,以及需要保留能够对运行中的程序进行调度的能力

即:高效、可控

本文作者:YaosGHC

本文链接:https://www.cnblogs.com/yaocy/p/17007751.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   YaosGHC  阅读(81)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起