操作系统(第四周 第二堂)

目录

回顾

进程运行

进程的创建

 进程的工作

举例

进程的删除

举例1(走到return 0结束)

举例2(利用exit(1)结束)

进程通信 

共享内存

生产者算法

消费者算法

消息传递 

定义

 算法实现

总结


回顾

上文的重点就两个内容:一、进程调度的理解;二、队列图以及调度程序

其中进程调度的理解有:一、进程本身角度调度理解;二、计算机整体角度理解进程管理和调度

队列图共有五个队列:I/O队列、中断队列、就绪队列、时间片过期队列、创建子进程队列

调度程序:长期调度程序、短期调度程序

进程运行

研究完进程调度,现在我们来研究进程的运行,研究进程运行前要先看进程是如何创建、删除的

一个进程的一生包括:创建、工作、被调度、删除

进程的创建

所有进程(除了Pid=0的初始进程)都是由父进程创建产生

定义:创建进程称为父进程,被创建进程称为子进程

每一个进程都采用进程标识符来唯一确定进程,子进程和父进程得pid不同

进程创建必备的两个指令:fork()、exec()

fork():

一、创建子进程,子进程和父进程完全相同(虚拟地址也相同,但是物理空间实际地址不同)。完全相同也意味着父进程fork后的程序计数器为fork函数结束位置,而子进程的程序计数器也为fork函数结束位置

二、在父进程中返回子进程的pid,在子进程中返回0

三、创建子进程也意味着要在内核中新建PCB(进程控制块)

exec():

一、以新程序来取代原进程的内存空间,包括程序、栈等进程所有的成分

二、exec后子进程就有了新的虚拟地址空间,可以认为“脱离”父进程的限制

三、exec后子进程的PID保持不变

 进程的工作

进程是并行工作的,即子进程被父进程创建后和父进程一起工作(父进程利用wait(NULL)除外,该方法让父进程在子进程结束后才工作)

举例

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

// 测试创建子进程函数 pid_t fork();
int main()
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        //创建子进程失败
        return -1;
    }
    if(0 == pid)
    {
        //子进程
        printf("I am child, my fork:%d\n", pid);
        printf("I am child, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am child, I have finished\n");  
    }
    else
    {
        //父进程 
        printf("I am father, my fork:%d\n", pid);      
        printf("I am father, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am father, I have finished\n");   
    }
    return 0;
}

执行结果为:

程序运行的关键点: 

1、父进程先运行:由于子进程由fork函数创建,所以需要耗费一些时间。

2、父子进程并行运行:父进程利用sleep函数休息时,可以看到子进程也输出了其值,说明父子进程目前共同在运行

3、fork返回值:父进程的fork返回值为子进程的pid,子进程的fork返回值为0

4、pid是进程标识符:子进程和父进程的pid的值不同

进程的删除

进程的删除主要有两种:

1、进程走到return 0 自己结束 

2、进程调用exit(1)结束

举例1(走到return 0结束)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

// 测试创建子进程函数 pid_t fork();
int main()
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        //创建子进程失败
        return -1;
    }
    if(0 == pid)
    {
        //子进程
        printf("I am child, my fork:%d\n", pid);
        printf("I am child, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am child, I have finished\n");  
    }
    else
    {
        //父进程 
        wait(NULL) 
        printf("I am father, my fork:%d\n", pid);      
        printf("I am father, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am father, I have finished\n");   
    }
    printf("%d 号进程已结束\n",pid);
    return 0;
}

 结果为:

关键点:在父进程中调用wait(NULL)函数让父进程在子进程结束后才开始运行 

举例2(利用exit(1)结束)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

// 测试创建子进程函数 pid_t fork();
int main()
{
    pid_t pid = fork();
    if(-1 == pid)
    {
        //创建子进程失败
        return -1;
    }
    if(0 == pid)
    {
        //子进程
        printf("I am child, my fork:%d\n", pid);
        printf("I am child, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am child, I have finished\n");
        exit(1);  
    }
    else
    {
        //父进程 
        wait(NULL) 
        printf("I am father, my fork:%d\n", pid);      
        printf("I am father, my pid:%d, ppid:%d\n",getpid(),getppid());
        sleep(5);
        printf("I am father, I have finished\n");   
    }
    printf("%d 号进程已结束\n",pid);
    return 0;
}

执行结果为:

关键点:子进程运行到exit函数后就自己删除自己,父进程开始运行 

进程通信 

操作系统内并发执行的进程可以是独立的也可以是协作的,协作完成的进程就涉及到进程间通信

进程通信主要有两种方式:1、共享内存  2、消息传递

图中关键点: 

1、共享内存没有内核参与,消息传递需要内核参与

2、共享内存速度快于消息传递,但是共享内存要避免内存冲突。在多处理器系统上,共享内存有高速缓存一致性的问题

共享内存

共享内存实现通信一定要面临一种情况:其中一个进程是消息的发送者,另一个进程是消息的接收者。

这样我们必须要设计算法保证:消息接受者知道消息发送者何时发送

生产者算法
while(true){
    while(((in+1)%BUFFER_SIZE)==out)
        ; //共享内存满了,就不能生产等待消费者进程拿
    buffer[in]=next_produced;
    in=(in+1))%BUFFER_SIZE
}
消费者算法
item next_consumed;
while(true){
    while(in==out)
        ;//此时共享内存是空的,无法拿东西
    next_comsumed=buffer[out];
    out=(out+1)%BUFFER_SIZE;

算法关键点: 

1、缓冲区最大值为BUFFER_SIZE-1(人为设定)

2、in+1==out设定为满,in==out设定为空

3、如果想要缓冲区最大值为BUFFER_SIZE,需要另设一个标志数用来记录缓冲区是满的还是空的。否则满的和空的都是in==out成立则无法区分

4、本质就是双指针实现,如上图所示

消息传递 

定义

关键点:

1、消息传递对于分布式环境(硬件不同)的两个进程的通信特别有用

2、消息传递必须通过系统调用完成,所以必须陷入内核

3、消息传递一共有两种通信方式,第一种是直接通信方式,第二种是间接通信方式

首先要知道一个消息是由消息头和消息体组成的,消息头里包括:发送进程ID、接收进程ID、消息类型、消息长度等格式化的信息(计算机网络中发送的“报文”其实就是一种格式化的消息)。

直接通信方式,如下:

发送进程把它想要发送的消息,通过发送原语发送给接收进程,然后这些消息会被放到接收进程的消息队列里面,接着接收进程通过接收原语一个个读取消息队列中的消息,这样发送进程和接收进程就实现了线程通信。

间接通信方式,如下:

对于间接通信方式,进程1会先通过发送原语把消息放到一个信箱中,然后进程2会通过接收原语从信箱中读取这些消息。

 算法实现

总结

本文到这里就结束啦~~这堂课的内容较为杂乱、复杂,但是学一学拓展一下知识是非常好的呀~~
如果觉得对你有帮助,辛苦友友点个赞哦~

知识来源:操作系统概念(黑宝书)、山东大学高晓程老师PPT及课上讲解。不要私下外传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/541182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python习题】若一个正整数的逆序数和它自身相等,则该整数称为回文数。编写程序,找出 1000以内的所有回文数。

题干 若一个正整数的逆序数和它自身相等,则该整数称为回文数。编写程序,找出 1000以内的所有回文数。 代码

深度学习可视化模型

文章目录 CAM&#xff08; https://arxiv.org/abs/1512.04150&#xff09;算法原理 Grad-CAM(梯度加权类激活映射)算法原理 Grad-CAMScoreCAMLayerCAM 目前深度学习网络的可视化可以分为&#xff1a; 可视化卷积核。可视化特征图。可视化激活热力图&#xff0c;也就是不同位置像…

【JAVASE】带你了解String类的常用方法和常见操作

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a; 再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1. 认识 String 类 2. 了解 String 类的基…

Flutter中间镂空的二维码扫描控件

1、UI效果图&#xff1a; 2、中间镂空UI&#xff1a; class CenterTransparentMask extends CustomClipper<Path> {final double? width;CenterTransparentMask({this.width});overridePath getClip(Size size) {final path Path()..addRect(Rect.fromLTWH(0, 0, size…

Go语言中如何正确使用 errgroup

不管是哪种编程语言,重新发明轮子都不是一个好主意。代码库重新实现如何启动多个goroutine并汇总错误也很常见。但是Go生态系统中的一个包旨在支持这种常见的用例。让我们来看看这个包并了解为什么它应该成为Go开发人员工具集的一部分。 golang.org/x是一个为标准库提供扩展的…

树上启发式合并(dsu on tree)学习

声明&#xff1a;本文部分内容摘自OI Wiki网站。详情可自行查看学习。 洛谷 P9233 题目实际上是蓝桥杯 2023 年 A 组省赛的一道题。题干大致的意思是&#xff0c;给定一个含有 n n n 个结点&#xff0c;并且以 1 1 1 为根的一棵树&#xff0c;每个节点 i i i 都有一个颜色 …

Spring框架第一篇(Spring概述与IOC思想)

文章目录 一、Spring概述二、Spring家族三、Spring Framework四、IOC思想五、IOC容器在Spring中的实现 一、Spring概述 Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。…

Harbor私有镜像仓库

Docker私有仓库Harbor ​ 前面学习了Docker及Dockerfile构建镜像&#xff0c;那么构建的镜像放在哪里才能被Docker容器快速获取到呢&#xff1f;我们知道&#xff0c;可以把镜像放入Docker Hub镜像仓库&#xff0c;但是Docker Hub是国外网站&#xff0c;一方面镜像放在Docker …

童年女神大盘点:谁是第一个让你心动的动漫女神?

每当提起我们的童年记忆&#xff0c;总有一抹亮丽的色彩来自于那些国产动漫中的女性角色&#xff0c;她们以其独特的魅力、鲜明的性格和卓越的才智&#xff0c;深深地烙印在了我们的心底&#xff0c;成为了一代人的集体回忆。今天&#xff0c;让我们一同回首&#xff0c;盘点那…

napi系列学习进阶篇——NAPI异步调用

简介 OpenHarmony Napi 标准系统异步接口实现支持Callback方式和Promise方式。标准系统异步接口实现规范要求&#xff0c;若引擎开启Promise特性支持&#xff0c;则异步方法必须同时支持Callback方式和Promise方式。使用哪种方式由应用开发者决定&#xff0c;通过是否传递Call…

全新4.0版本圈子社交论坛系统 ,可打包小程序,于TP6+uni-app 全开源 可打包小程序app uniapp前端+全开源+独立版

简述 首先 圈子系统的核心是基于共同的兴趣或爱好将用户聚集在一起&#xff0c;这种设计使得用户能够迅速找到与自己有共同话题和兴趣的人。 其次 圈子系统提供了丰富的社交功能&#xff0c;如发帖、建圈子、发活动等&#xff0c;并且支持小程序授权登录、H5和APP等多种形式…

R语言记录过程

如何使用这个函数as.peakData 函数构造过程 出现问题是缺少函数的问题 up不告诉我&#xff0c;这里是代表c,h,o的值&#xff0c;你从里面获取把值&#xff0c;设置成c,h,o就可以了 现在开始测试参数 第一次 startRow : 开始查找数据的第一行。不管startRow的值是多少&#xff…

openstack-镜像服务 3

Glance镜像服务 创建glacnce数据库 创建glance用户并创建服务实体及api端点 安装glance软件包 修改配置文件 同步到数据库 设置开机自启并查看日志目录 使用测试镜像验证服务

10分钟带你学会配置DNS服务正反向解析

正向解析 服务端IP客户端IP网址192.168.160.134192.168.160.135www.openlab.com 一、首先做准备工作&#xff1a; 关闭安全软件&#xff0c;关闭防火墙&#xff0c;下载bind软件 [rootserver ~]# setenforce 0 [rootserver ~]# systemctl stop firewalld [rootserver ~]# y…

LeetCode题练习与总结:最小路径和--64

一、题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输出…

超级详细的JDBC和数据库连接池讲解

文章目录 JDBC简介概念本质好处 JDBC快速入门JDBC中API详解DriverManager驱动管理类作用注册驱动获取连接 Connection数据库连接对象作用获取执行SQL的对象事务管理 Statement作用执行SQL语句 ResultSet原理使用步骤 PreparedStatementSQL注入获取对象操作步骤 原理好处 JDBC工…

项目管理-项目问题及需求解决要点

综上所述&#xff1a;在项目管理过程中&#xff0c;项目问题和需求逐渐增多&#xff0c;要不断的适应项目的种种&#xff0c;要想到如果没有问题要解决了&#xff0c;你的价值体现在哪里&#xff1f;要这样想&#xff0c;风险也是机会&#xff0c;所以问题等等也是自己的机会&a…

CopyTranslator下载地址及安装教程

CopyTranslator是一款免费且开源的机器翻译工具&#xff0c;旨在提供快速、便捷的翻译服务。它采用了先进的神经网络机器翻译技术&#xff0c;能够提供准确、流畅的翻译结果。 CopyTranslator的特点和功能如下&#xff1a; 多语言翻译&#xff1a;支持多种常见的语言对&#…

Unity让地图素材遮挡人物

点击编辑/项目设置/图形&#xff0c;透明度排序模式设置自定义轴&#xff0c;透明度排序轴Y设置为1其他为0。 此时人物和地图素材的图层排序相等&#xff0c;当人物的高度大于地图素材时&#xff0c;人物则被遮挡。

【零基础学数据结构】双向链表

1.双向链表的概念 1.1头节点 1.2带头双向循环链表 注意&#xff1a; 哨兵位创建后&#xff0c;首尾连接自己 1.3双链表的初始化 // 双向链表的初始化 void ListInit(ListNode** pphead) {// 给双链表创建一个哨兵位*pphead ListBuyNode(-1); } 2.双向链表的打印 // 双向…