5.3 协作式多任务操作系统
51—TinyOS51嵌入式操作系统微小内核
与中止函数about()和退出函数exit()相比,初看 起来,goto语句处理异常更可行,但是,goto语句 只能在函数内部跳转,即不能从一个函数直接跳转 到另一个函数。
为此,标准C函数库提供了setjmp()和longjmp() 函数,setjmp()函数相当于非局部标号,longjmp() 函数相当于goto的作用,从而解决了从一个函数直 接跳转到另一个函数的问题,即非局部跳转。头文 件<setjmp.h>申明了这些函数及同时所需的jmp_buf 数据类型。
如下图所示:
8
第5章 TinyOS51嵌入式操作系统 时间轮询调度为每个任务提供同份额的cpu执行
时间。由于纯粹的时间轮询调度不能满足实时性系统 要求,取而代之的是基于优先级抢占式调度扩充时间 轮询调度,即对同样优先级的任务使用时间片获得相 等的cpu分配时间,不同优先级的具有抢占权。
9
第5章 TinyOS51嵌入式操作系统
就绪状态 ;运行状态; 阻塞状态
10
第5章 TinyOS51嵌入式操作系统
就绪状态:当一个任务创立并准备运行时,内核将其放入 就绪状态。但不能运行,因为有一个更高优先级的任务在 执行,内核调度器根据优先级决定哪个任务先迁移到运行 状态,但处于就绪状态的任务不能直接迁移到阻塞状态。
运行状态:操作系统可让处于运行状态的低优先级任务暂 停运行,转而执行另一个处于就绪状态的高优先级任务, 这样正运行的任务就从运行状态迁移到了就绪状态。
SP
变化。
addr15~addr8
addr7~addr0
图 5.7
上下文信息 23
第5章 TinyOS51嵌入式操作系统
计算机操作系统中的多任务处理与调度算法
计算机操作系统中的多任务处理与调度算法随着计算机应用场景的多样化,多任务处理成为操作系统中必不可少的一部分。
在计算机操作系统中,多任务处理涉及到如何有效地利用计算机资源,使多个任务并发执行,以提高系统的运行效率和资源利用率。
为了实现多任务处理,操作系统需要采用相应的调度算法来决定任务的执行顺序和时间片分配,以确保系统的公平性、高效性和稳定性。
一、多任务处理的概念与分类多任务处理是指操作系统能够同时运行多个任务,使得这些任务在用户看来就像在同时执行一样。
根据任务之间的关系,多任务处理可以分为协作式和抢占式两种模式。
协作式多任务处理模式要求各个任务之间主动地协调与合作,每个任务必须主动释放CPU资源,否则其他任务无法执行。
这种模式的优点是实现简单,但是一旦有任务崩溃或者无限循环,将会导致整个系统崩溃。
抢占式多任务处理模式则由操作系统来决定任务的执行优先级和时间片分配,可以主动抢占正在执行的任务,使其他任务有机会执行。
这种模式的优点是能够保证系统的稳定性和响应性,但实现相对复杂。
二、常见的调度算法1. 先来先服务(FCFS)先来先服务是一种简单的调度算法,按照任务到达的顺序进行调度,即先到达的任务优先执行,直到该任务执行完毕或者发生阻塞。
优点是公平,缺点是无法应对执行时间较长的任务,可能导致其他任务的等待时间较长。
2. 最短作业优先(SJF)最短作业优先调度算法是根据任务的执行时间来进行调度,执行时间最短的任务先执行。
优点是能够减少平均等待时间,提高系统的执行效率,缺点是对执行时间较长的任务不公平。
3. 时间片轮转(RR)时间片轮转调度算法将任务划分为等长的时间片,每个任务按照时间片的顺序轮流执行,每个任务执行一个时间片后切换至下一个任务,循环往复。
优点是公平,能够保证每个任务都能获得一定的执行时间,同时也能快速响应用户的操作。
缺点是对于执行时间较长的任务,会造成一定的延迟。
4. 优先级调度优先级调度算法根据任务的优先级来进行调度,优先级高的任务先执行。
操作系统中的多任务处理
操作系统中的多任务处理
在操作系统中,多任务处理是指操作系统能够同时管理和执行多个任务的能力。
这种能力使得计算机系统能够更高效地利用资源,提高系统的响应速度和性能。
多任务处理有两种主要的方式:并发和并行。
并发是指在同一时间段内执行多
个任务,而并行是指在同一时刻执行多个任务。
在操作系统中,通常会采用并发的方式来实现多任务处理。
为了实现多任务处理,操作系统会使用一些技术和机制来管理和调度任务。
其
中最常见的技术包括进程管理、线程管理和调度算法。
进程是程序的执行实例,每个进程都有自己的独立地址空间和资源。
操作系统
通过进程管理来创建、删除和调度进程,确保它们能够正确地运行。
线程是在进程内部执行的轻量级任务,多个线程可以共享进程的资源,提高系统的并发性能。
线程管理负责创建和调度线程,保证它们能够协同工作完成任务。
调度算法是操作系统用来选择下一个要执行的任务的规则。
常见的调度算法包
括先来先服务、最短作业优先、时间片轮转和优先级调度等。
这些算法可以根据任务的特性和系统的需求来选择合适的调度方式,确保系统能够高效地完成任务。
通过多任务处理,操作系统能够更好地响应用户的请求,提高系统的利用率和
性能。
同时,多任务处理也能够提高系统的稳定性和可靠性,确保系统能够持续运行。
总的来说,操作系统中的多任务处理是一种重要的技朧,它可以提高系统的效
率和性能,提升用户体验,是现代计算机系统不可或缺的一部分。
通过合理地管理和调度任务,操作系统能够更好地满足用户的需求,提高系统的运行效率和稳定性。
操作系统的多任务处理
操作系统的多任务处理操作系统是计算机硬件与应用程序之间的关系管理者,它负责调度和管理计算机资源,为应用程序提供必要的支持和服务。
而多任务处理作为操作系统的一个重要特性,在提高计算机效率和资源利用率方面具有重要作用。
本文将探讨操作系统的多任务处理机制及其应用。
一、多任务处理概述多任务处理是指操作系统能够同时执行多个任务,使得用户感觉像是多个任务在同时进行。
它分为并行处理和时间片轮转两种方式。
1. 并行处理并行处理是指在多处理器或多核处理器系统中,多个任务能够同时执行。
每个处理器或核心负责一个任务,通过并行计算、并发执行提高了整个系统的计算速度和效率。
2. 时间片轮转时间片轮转是指操作系统按照时间片(一小段时间)轮流分配给各个任务,使得多个任务可以交替执行。
每个任务在一个时间片内执行一段时间,然后让出CPU资源给其他任务,通过快速切换任务的方式,让用户感觉多个任务在同时进行。
二、多任务处理的实现机制为了实现多任务处理,操作系统需要具备以下几个重要的机制:1. 进程管理进程管理是指操作系统对进程的创建、调度、状态转换和销毁等操作。
操作系统为每个任务分配一个独立的进程,并利用进程调度算法按照一定的优先级和策略进行调度,确保每个任务都能够得到公平的执行机会。
2. 任务切换多任务处理需要操作系统具备快速任务切换的能力,以实现任务间的流畅转换。
当一个任务的时间片用尽或发生阻塞时,操作系统会迅速切换到下一个任务,保证多个任务都能够得到执行。
3. 资源分配操作系统需要合理地分配和管理CPU、内存、外设等计算机资源,以满足多个任务对资源的需求。
通过资源分配策略,操作系统能够为每个任务提供所需的资源,并确保资源的公平分配和高效利用。
三、多任务处理的应用多任务处理在操作系统中广泛应用于各种场景,提供了更加灵活和高效的计算环境。
1. 多用户环境在多用户环境下,多任务处理允许多个用户同时进行各自的操作和任务。
每个用户可以独立地运行自己的应用程序,而不会干扰其他用户的操作。
操作系统的多任务与多线程支持
操作系统的多任务与多线程支持操作系统是计算机系统中最为核心的软件之一,它负责管理和控制计算机中的硬件资源以及运行程序的执行。
多任务和多线程是操作系统的两个重要特性,它们在提高计算机系统性能、资源利用率和用户体验等方面发挥着重要作用。
一、多任务支持多任务是指操作系统能够同时运行多个程序,并且给用户的感觉是这些程序在同时进行。
操作系统通过轮询或者中断的方式在不同程序之间进行切换,为每个程序分配一定的执行时间片,给用户一种同时运行多个程序的错觉。
多任务支持使得用户能够方便地在计算机上同时运行多个应用程序,例如同时打开多个浏览器窗口、编辑文档和播放音乐等。
同时,多任务也提高了计算机系统的资源利用率,因为在一个时间片内,操作系统可以将执行权交给其他程序,使得系统中的计算资源得到充分利用。
在多任务系统中,操作系统通过调度算法来决定每个程序的执行顺序和时间片大小。
常见的调度算法有先来先服务(FCFS)、时间片轮转、优先级调度等。
这些算法根据不同的系统需求和优先级策略来进行选择。
二、多线程支持多线程是指在一个程序内部,能够同时执行多个子任务或者称之为线程的部分。
多线程在一个进程内共享同一块内存空间,各个线程之间可以共享数据和资源,使得程序的并发度增加,进而提高系统的吞吐量和响应速度。
多线程支持使得程序在执行过程中能够以更高效的方式处理并发任务,因为线程之间切换的开销要远远小于进程之间的切换。
此外,多线程也能够简化程序的编写,通过将程序拆分为多个线程来处理不同的任务,使得程序的结构更加清晰和模块化。
在多线程系统中,操作系统需要提供线程的管理和调度功能。
通过线程调度算法,操作系统能够决定哪些线程先被执行、如何切换线程以及如何调整不同线程之间的优先级。
常见的线程调度算法有抢占式调度、协同式调度和时间片轮转等。
三、多任务与多线程的关系多任务和多线程是操作系统中相关但又具有不同概念和作用的特性。
多任务是指操作系统能够同时运行多个程序,而多线程是指一个程序内部可以同时执行多个线程。
51单片机多任务的原理及其实现
51单片机多任务操作系统的原理与实现51单片机多任务操作系统的原理与实现-- 一个超轻量级的操作系统前言想了很久,要不要写这篇文章最后觉得对操作系统感兴趣的人还是很多,写吧.我不一定能造出玉,但我可以抛出砖.包括我在内的很多人都对51使用操作系统呈悲观态度,因为51的片上资源太少.但对于很多要求不高的系统来说,使用操作系统可以使代码变得更直观,易于维护,所以在51上仍有操作系统的生存机会.流行的uCos,Tiny51等,其实都不适合在2051这样的片子上用,占资源较多,唯有自已动手,以不变应万变,才能让51也有操作系统可用.这篇贴子的目的,是教会大家如何现场写一个OS,而不是给大家提供一个OS版本.提供的所有代码,也都是示例代码,所以不要因为它没什么功能就说LAJI之类的话.如果把功能写全了,一来估计你也不想看了,二来也失去灵活性没有价值了.下面的贴一个示例出来,可以清楚的看到,OS本身只有不到10行源代码,编译后的目标代码60字节,任务切换消耗为20个机器周期.相比之下,KEIL内嵌的TINY51目标代码为800字节,切换消耗100~700周期.唯一不足之处是,每个任务要占用掉十几字节的堆栈,所以任务数不能太多,用在128B内存的51里有点难度,但对于52来说问题不大.这套代码在36M主频的STC12C4052上实测,切换任务仅需2uS.#include <>#define MAX_TASKS 2 须和实际任务数一至#define MAX_TASK_DEP 12 低不得少于2个,保守值为12.unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];unsigned char task_id; 指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误.void task_load(unsigned int fn, unsigned char tid){task_sp[tid] = task_stack[tid] + 1;task_stack[tid][0] = (unsigned int)fn & 0xff;task_stack[tid][1] = (unsigned int)fn >> 8;}用该宏后,将永不返回.#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}/*======================以下为测试代码======================*/void task1(){static unsigned char i;while(1){i++;task_switch(); 么是操作系统?人脑比较容易接受"类比"这种表达方式,我就用"公交系统"来类比"操作系统"吧.当我们要解决一个问题的时候,是用某种处理手段去完成它,这就是我们常说的"方法",计算机里叫"程序"(有时候也可以叫它"算法").以出行为例,当我们要从A地走到B地的时候,可以走着去,也可以飞着去,可以走直线,也可以绕弯路,只要能从A地到B地,都叫作方法.这种从A地到B的需求,相当于计算机里的"任务",而实现从A地到B地的方法,叫作"任务处理流程"很显然,这些走法中,并不是每种都合理,有些傻子都会采用的,有些是傻子都不采会用的.用计算机的话来说就是,有的任务处理流程好,有的任务处理流程好,有的处理流程差.可以归纳出这么几种真正算得上方法的方法:有些走法比较快速,适合于赶时间的人;有些走法比较省事,适合于懒人;有些走法比较便宜,适合于穷人.用计算机的话说就是,有些省CPU,有些流程简单,有些对系统资源要求低.现在我们可以看到一个问题:如果全世界所有的资源给你一个人用(单任务独占全部资源),那最适合你需求的方法就是好方法.但事实上要外出的人很多,例如10个人(10个任务),却只有1辆车(1套资源),这叫作"资源争用".如果每个人都要使用最适合他需求的方法,那司机就只好给他们一人跑一趟了,而在任一时刻里,车上只有一个乘客.这叫作"顺序执行",我们可以看到这种方法对系统资源的浪费是严重的.如果我们没有法力将1台车变成10台车来送这10个人,就只好制定一些机制和约定,让1台车看起来像10台车,来解决这个问题的办法想必大家都知道,那就是制定公交线路.最简单的办法是将所有旅客需要走的起点与终点串成一条线,车在这条线上开,乘客则自已决定上下车.这就是最简单的公交线路.它很差劲,但起码解决客人们对车争用.对应到计算机里,就是把所有任务的代码混在一起执行.这样做既不优异雅,也没效率,于是司机想了个办法,把这些客户叫到一起商量,将所有客人出行的起点与终点罗列出来,统计这些线路的使用频度,然后制定出公交线路:有些路线可以合并起来成为一条线路,而那些不能合并的路线,则另行开辟行车车次,这叫作"任务定义".另外,对于人多路线,车次排多点,时间上也优先安排,这叫作"任务优先级".经过这样的安排后,虽然仍只有一辆车,但运载能力却大多了.这套车次/路线的按排,就是一套"公交系统".哈,知道什么叫操作系统了吧它也就是这么样的一种约定.操作系统:我们先回过头归纳一下:汽车系统资源.主要指的是CPU,当然还有其它,比如内存,定时器,中断源等.客户出行任务正在走的路线进程一个一个的运送旅客顺序执行同时运送所有旅客多任务并行按不同的使用频度制定路线并优先跑较繁忙的路线任务优先级计算机内有各种资源,单从硬件上说,就有CPU,内存,定时器,中断源,I/O端口等.而且还会派生出来很多软件资源,例如消息池.操作系统的存在,就是为了让这些资源能被合理地分配.最后我们来总结一下,所谓操作系统,以我们目前权宜的理解就是:为"解决计算机资源争用而制定出的一种约定".二.51上的操作系统对于一个操作系统来说,最重要的莫过于并行多任务.在这里要澄清一下,不要拿当年的DOS来说事,时代不同了.况且当年IBM和小比尔着急将PC搬上市,所以才抄袭PLM(好象是叫这个名吧记不太清)搞了个今天看来很"粗制滥造"的DOS出来.看看当时真正的操作系统---UNIX,它还在纸上时就已经是多任务的了.对于我们PC来说,要实现多任务并不是什么问题,但换到MCU却很头痛:1.系统资源少在PC上,CPU主频以G为单位,内存以GB为单位,而MCU的主频通常只有十几M,内存则是Byts.在这么少的资源上同时运行多个任务,就意味着操作系统必须尽可能的少占用硬件资源.2.任务实时性要求高PC并不需要太关心实时性,因为PC上几乎所有的实时任务都被专门的硬件所接管,例如所有的声卡网卡显示上都内置有DSP以及大量的缓存.CPU只需坐在那里指手划脚告诉这些板卡如何应付实时信息就行了.而MCU不同,实时信息是靠CPU来处理的,缓存也非常有限,甚至没有缓存.一旦信息到达,CPU必须在极短的时间内响应,否则信息就会丢失.就拿串口通信来举例,在标准的PC架构里,巨大的内存允许将信息保存足够长的时间.而对于MCU来说内存有限,例如51仅有128字节内存,还要扣除掉寄存器组占用掉的8~32个字节,所以通常都仅用几个字节来缓冲.当然,你可以将数据的接收与处理的过程合并,但对于一个操作系统来说,不推荐这么做.假定以115200bps通信速率向MCU传数据,则每个字节的传送时间约为9uS,假定缓存为8字节,则串口处理任务必须在70uS内响应.这两个问题都指向了同一种解决思路:操作系统必须轻量轻量再轻量,最好是不占资源(那当然是做梦啦).可用于MCU的操作系统很多,但适合51(这里的51专指无扩展内存的51)几乎没有.前阵子见过一个"圈圈操作系统",那是我所见过的操作系统里最轻量的,但仍有改进的余地.很多人认为,51根本不适合使用操作系统.其实我对这种说法并不完全接受,否则也没有这篇文章了.我的看法是,51不适合采用"通用操作系统".所谓通用操作系统就是,不论你是什么样的应用需求,也不管你用什么芯片,只要你是51,通通用同一个操作系统.这种想法对于PC来说没问题,对于嵌入式来说也不错,对AVR来说还凑合,而对于51这种"贫穷型"的MCU来说,不行.怎样行量体裁衣,现场根据需求构建一个操作系统出来!看到这里,估计很多人要翻白眼了,大体上两种:1.操作系统那么复杂,说造就造,当自已是神了2.操作系统那么复杂,现场造一个会不会出BUG哈哈,看清楚了问题出在"复杂"上面,如果操作系统不复杂,问题不就解决了事实上,很多人对操作系统的理解是片面的,操作系统不一定要做得很复杂很全面,就算仅个多任务并行管理能力,你也可以称它操作系统.只要你对多任务并行的原理有所了解,就不难现场写一个出来,而一旦你做到了这一点,为各任务间安排通信约定,使之发展成一个为你的应用系统量身定做的操作系统也就不难了.为了加深对操作系统的理解,可以看一看<<演变>>这份PPT,让你充分了解一个并行多任务是如何一步步从顺序流程演变过来的.里面还提到了很多人都在用的"状态机",你会发现操作系统跟状态机从原理上其实是多么相似.会用状态机写程序,都能写出操作系统.三.我的第一个操作系统直接进入主题,先贴一个操作系统的示范出来.大家可以看到,原来操作系统可以做得么简单.当然,这里要申明一下,这玩意儿其实算不上真正的操作系统,它除了并行多任务并行外根本没有别的功能.但凡事都从简单开始,搞懂了它,就能根据应用需求,将它扩展成一个真正的操作系统.好了,代码来了.将下面的代码直接放到KEIL里编译,在每个task()函数的"task_switch();"那里打上断点,就可以看到它们的确是"同时"在执行的.#include <>#define MAX_TASKS 2 须和实际任务数一至#define MAX_TASK_DEP 12 低不得少于2个,保守值为12.unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];unsigned char task_id; 指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误.void task_load(unsigned int fn, unsigned char tid){task_sp[tid] = task_stack[tid] + 1;task_stack[tid][0] = (unsigned int)fn & 0xff;task_stack[tid][1] = (unsigned int)fn >> 8;}用该宏后,将永不返回.#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}/*==================以下为测试代码=====================*/void task1(){static unsigned char i;while(1){i++;task_switch();现在来看看这个多任务系统的原理:这个多任务系统准确来说,叫作"协同式多任务".所谓"协同式",指的是当一个任务持续运行而不释放资源时,其它任务是没有任何机会和方式获得运行机会,除非该任务主动释放CPU.在本例里,释放CPU是靠task_switch()来完成的.task_switch()函数是一个很特殊的函数,我们可以称它为"任务切换器".要清楚任务是如何切换的,首先要回顾一下堆栈的相关知识.有个很简单的问题,因为它太简单了,所以相信大家都没留意过:我们知道,不论是CALL还是JMP,都是将当前的程序流打断,请问CALL和JMP的区别是什么你会说:CALL可以RET,JMP不行.没错,但原因是啥呢为啥CALL过去的就可以用RET跳回来,JMP过去的就不能用RET来跳回呢很显然,CALL通过某种方法保存了打断前的某些信息,而在返回断点前执行的RET指令,就是用于取回这些信息.不用多说,大家都知道,"某些信息"就是PC指针,而"某种方法"就是压栈.很幸运,在51里,堆栈及堆栈指针都是可被任意修改的,只要你不怕死.那么假如在执行RET前将堆栈修改一下会如何往下看:当程序执行CALL后,在子程序里将堆栈刚才压入的断点地址清除掉,并将一个函数的地址压入,那么执行完RET后,程序就跳到这个函数去了.事实上,只要我们在RET前将堆栈改掉,就能将程序跳到任务地方去,而不限于CALL里压入的地址.重点来了......首先我们得为每个任务单独开一块内存,这块内存专用于作为对应的任务的堆栈,想将CPU交给哪个任务,只需将栈指针指向谁内存块就行了.接下来我们构造一个这样的函数:当任务调用该函数时,将当前的堆栈指针保存一个变量里,并换上另一个任务的堆栈指针.这就是任务调度器了.OK了,现在我们只要正确的填充好这几个堆栈的原始内容,再调用这个函数,这个任务调度就能运行起来了.那么这几个堆栈里的原始内容是哪里来的呢这就是"任务装载"函数要干的事了.在启动任务调度前将各个任务函数的入口地址放在上面所说的"任务专用的内存块"里就行了!对了,顺便说一下,这个"任务专用的内存块"叫作"私栈",私栈的意思就是说,每个任务的堆栈都是私有的,每个任务都有一个自已的堆栈.话都说到这份上了,相信大家也明白要怎么做了:1.分配若干个内存块,每个内存块为若干字节:这里所说的"若干个内存块"就是私栈,要想同时运行几少个任务就得分配多少块.而"每个子内存块若干字节"就是栈深.记住,每调一层子程序需要2字节.如果不考虑中断,4层调用深度,也就是8字节栈深应该差不多了.unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP]当然,还有件事不能忘,就是堆指针的保存处.不然光有堆栈怎么知道应该从哪个地址取数据啊unsigned char idata task_sp[MAX_TASKS]上面两项用于装任务信息的区域,我们给它个概念叫"任务槽".有些人叫它"任务堆",我觉得还是"槽"比较直观对了,还有任务号.不然怎么知道当前运行的是哪个任务呢unsigned char task_id当前运行存放在1号槽的任务时,这个值就是1,运行2号槽的任务时,这个值就是2....2.构造任务调度函函数:void task_switch(){task_sp[task_id] = SP;}3.装载任务:将各任务的函数地址的低字节和高字节分别入在task_stack[任务号][0]和task_stack[任务号][1]中:为了便于使用,写一个函数: task_load(函数名, 任务号)void task_load(unsigned int fn, unsigned char tid){task_sp[tid] = task_stack[tid] + 1;task_stack[tid][0] = (unsigned int)fn & 0xff;task_stack[tid][1] = (unsigned int)fn >> 8;}4.启动任务调度器:将栈指针指向任意一个任务的私栈,执行RET指令.注意,这可很有学问的哦,没玩过堆栈的人脑子有点转不弯:这一RET,RET到哪去了嘿嘿,别忘了在RET前已经将堆栈指针指向一个函数的入口了.你别把RET看成RET,你把它看成是另一种类型的JMP就好理解了.SP = task_sp[任务号];return;做完这4件事后,任务"并行"执行就开始了.你可以象写普通函数一个写任务函数,只需(目前可以这么说)注意在适当的时候(例如以前调延时的地方)调用一下task_switch(),以让出CPU控制权给别的任务就行了.最后说下效率问题.这个多任务系统的开销是每次切换消耗20个机器周期(CALL和RET都算在内了),贵吗不算贵,对于很多用状态机方式实现的多任务系统来说,其实效率还没这么高--- case switch和if()可不像你想像中那么便宜.关于内存的消耗我要说的是,当然不能否认这种多任务机制的确很占内存.但建议大家不要老盯着编译器下面的那行字"DATA = XXXbyte".那个值没意义,堆栈没算进去.关于比较省内存多任务机制,我将来会说到.概括来说,这个多任务系统适用于实时性要求较高而内存需求不大的应用场合,我在运行于36M主频的STC12C4052上实测了一把,切换一个任务不到3微秒.下回我们讲讲用KEIL写多任务函数时要注意的事项.下下回我们讲讲如何增强这个多任务系统,跑步进入操作系统时代.四.用KEIL写多任务系统的技巧与注意事项C51编译器很多,KEIL是其中比较流行的一种.我列出的所有例子都必须在KEIL 中使用.为何不是因为KEIL好所以用它(当然它的确很棒),而是因为这里面用到了KEIL的一些特性,如果换到其它编译器下,通过编译的倒不是问题,但运行起来可能是堆栈错位,上下文丢失等各种要命的错误,因为每种编译器的特性并不相同.所以在这里先说清楚这一点.但是,我开头已经说了,这套帖子的主要目的是阐述原理,只要你能把这几个例子消化掉,那么也能够自已动手写出适合其它编译器的OS.好了,说说KEIL的特性吧,先看下面的函数:sbit sigl = P1^7;void func1(){register char data i;i = 5;do{sigl = !sigl;}while(--i);}你会说,这个函数没什么特别的嘛!呵呵,别着急,你将它编译了,然后展开汇编代码再看看:193: void func1(){194: register char data i;195: i = 5;C:0x00C3 7F05 MOV R7,#0x05196: do{197: sigl = !sigl;C:0x00C5 B297 CPL sigl198: }while(--i);C:0x00C7 DFFC DJNZ R7,C:00C5199: }C:0x00C9 22 RET看清楚了没这个函数里用到了R7,却没有对R7进行保护!有人会跳起来了:这有什么值得奇怪的,因为上层函数里没用到R7啊.呵呵,你说的没错,但只说对了一半:事实上,KEIL编译器里作了约定,在调子函数前会尽可能释放掉所有寄存器.通常性况下,除了中断函数外,其它函数里都可以任意修改所有寄存器而无需先压栈保护(其实并不是这样,但现在暂时这样认为,饭要一口一口吃嘛,我很快会说到的).这个特性有什么用呢有!当我们调用任务切换函数时,要保护的对象里可以把所有的寄存器排除掉了,就是说,只需要保护堆栈即可!现在我们回过头来看看之前例子里的任务切换函数:void task_switch(){task_sp[task_id] = SP;}看到没,一个寄存器也没保护,展开汇编看看,的确没保护寄存器.好了,现在要给大家泼冷水了,看下面两个函数:void func1(){register char data i;i = 5;do{sigl = !sigl;}while(--i);}void func2(){register char data i;i = 5;do{func1();}while(--i);}父函数fun2()里调用func1(),展开汇编代码看看: 193: void func1(){194: register char data i;195: i = 5;C:0x00C3 7F05 MOV R7,#0x05 196: do{197: sigl = !sigl; C:0x00C5 B297 CPL sigl 198: }while(--i);C:0x00C7 DFFC DJNZ R7,C:00C5 199: }C:0x00C9 22 RET200: void func2(){201: register char data i;202: i = 5;C:0x00CA 7E05 MOV R6,#0x05 203: do{204: func1();C:0x00CC 11C3 ACALL func1(C:00C3) 205: }while(--i);C:0x00CE DEFC DJNZ R6,C:00CC206: }C:0x00D0 22 RET看清楚没函数func2()里的变量使用了寄存器R6,而在func1和func2里都没保护.听到这里,你可能又要跳一跳了:func1()里并没有用到R6,干嘛要保护没错,但编译器是怎么知道func1()没用到R6的呢是从调用关系里推测出来的.一点都没错,KEIL会根据函数间的直接调用关系为各函数分配寄存器,既不用保护,又不会冲突,KEIL好棒哦!!等一下,先别高兴,换到多任务的环境里再试试:void func1(){register char data i;i = 5;do{sigl = !sigl;}while(--i);}void func2(){register char data i;i = 5;do{sigl = !sigl;}while(--i);}展开汇编代码看看:193: void func1(){194: register char data i;195: i = 5;C:0x00C3 7F05 MOV R7,#0x05 196: do{197: sigl = !sigl; C:0x00C5 B297 CPL sigl 198: }while(--i);C:0x00C7 DFFC DJNZ R7,C:00C5 199: }C:0x00C9 22 RET200: void func2(){201: register char data i;202: i = 5;C:0x00CA 7F05 MOV R7,#0x05203: do{204: sigl = !sigl;C:0x00CC B297 CPL sigl205: }while(--i);C:0x00CE DFFC DJNZ R7,C:00CC206: }C:0x00D0 22 RET看到了吧哈哈,这回神仙也算不出来了.因为两个函数没有了直接调用的关系,所以编译器认为它们之间不会产生冲突,结果分配了一对互相冲突的寄存器,当任务从func1()切换到func2()时,func1()中的寄存器内容就给破坏掉了.大家可以试着去编译一下下面的程序:sbit sigl = P1^7;void func1(){register char data i;i = 5;do{sigl = !sigl;task_switch();} while (--i);}void func2(){register char data i;i = 5;do{sigl = !sigl;task_switch();}while(--i);}我们这里只是示例,所以仍可以通过手工分配不同的寄存器避免寄存器冲突,但在真实的应用中,由于任务间的切换是非常随机的,我们无法预知某个时刻哪个寄存器不会冲突,所以分配不同寄存器的方法不可取.那么,要怎么办呢这样就行了:sbit sigl = P1^7;void func1(){static char data i;while(1){i = 5;do{sigl = !sigl;task_switch();}while(--i);}}void func2(){static char data i;while(1){i = 5;do{sigl = !sigl;task_switch();}while(--i);}}将两个函数中的变量通通改成静态就行了.还可以这么做:sbit sigl = P1^7;void func1(){register char data i;while(1){i = 5;do{sigl = !sigl;}while(--i);task_switch();}}void func2(){register char data i;while(1){i = 5;do{sigl = !sigl;}while(--i);task_switch();}}即,在变量的作用域内不切换任务,等变量用完了,再切换任务.此时虽然两个任务仍然会互相破坏对方的寄存器内容,但对方已经不关心寄存器里的内容了.以上所说的,就是"变量覆盖"的问题.现在我们系统地说说关于"变量覆盖".变量分两种,一种是全局变量,一种是局部变量(在这里,寄存器变量算到局部变量里).对于全局变量,每个变量都会分配到单独的地址.而对于局部变量,KEIL会做一个"覆盖优化",即没有直接调用关系的函数的变量共用空间.由于不是同时使用,所以不会冲突,这对内存小的51来说,是好事.但现在我们进入多任务的世界了,这就意味着两个没有直接调用关系的函数其实是并列执行的,空间不能共用了.怎么办呢一种笨办法是关掉覆盖优化功能.呵呵,的确很笨.比较简单易行一个解决办法是,不关闭覆盖优化,但将那些在作用域内需要跨越任务(换句话说就是在变量用完前会调用task_switch()函数的)变量通通改成静态(static)即可.这里要对初学者提一下,"静态"你可以理解为"全局",因为它的地址空间一直保留,但它又不是全局,它只能在定义它的那个花括号对{}里访问.静态变量有个副作用,就是即使函数退出了,仍会占着内存.所以写任务函数的时候,尽量在变量作用域结束后才切换任务,除非这个变量的作用域很长(时间上长),会影响到其它任务的实时性.只有在这种情况下才考虑在变量作用域内跨越任务,并将变量申明为静态.事实上,只要编程思路比较清析,很少有变量需要跨越任务的.就是说,静态变量并不多.说完了"覆盖"我们再说说"重入".所谓重入,就是一个函数在同一时刻有两个不同的进程复本.对初学者来说可能不好理解,我举个例子吧:有一个函数在主程序会被调用,在中断里也会被调用,假如正当在主程序里调用时,中断发生了,会发生什么情况void func1(){static char data i;i = 5;do{sigl = !sigl;}while(--i);}假定func1()正执行到i=3时,中断发生,一旦中断调用到func1()时,i的值就被破坏了,当中断结束后,i == 0.以上说的是在传统的单任务系统中,所以重入的机率不是很大.但在多任务系统中,很容易发生重入,看下面的例子:void func1()....delay();....}void func2(){....delay();....}void delay(){static unsigned char i;申明为static会发生重入问题.麻烦啊for(i=0;i<10;i++)task_switch();两个并行执行的任务都调用了delay(),这就叫重入.问题在于重入后的两个复本都依赖变量i来控制循环,而该变量跨越了任务,这样,两个任务都会修改i值了.重入只能以防为主,就是说尽量不要让重入发生,比如将代码改成下面的样子:#define delay() {static unsigned char i; for(i=0;i<10;i++)task_switch();}void func1(){....delay();....}void func2(){....delay();....用宏来代替函数,就意味着每个调用处都是一个独立的代码复本,那么两个delay实际使用的内存地址也就不同了,重入问题消失.但这种方法带来的问题是,每调用一次delay(),都会产生一个delay的目标代码,如果delay的代码很多,那就会造成大量的rom空间占用.有其它办法没本人所知有限,只有最后一招了:void delay() reentrant{unsigned char i;for(i=0;i<10;i++)task_switch();}加入reentrant申明后,该函数就可以支持重入.但小心使用,申明为重入后,函数效率极低!最后附带说下中断.因为没太多可说的,就不单独开章了.中断跟普通的写法没什么区别,只不过在目前所示例的多任务系统里因为有堆栈的压力,所以要使用using来减少对堆栈的使用(顺便提下,也不要调用子函数,同样是为了减轻堆栈压力)用using,必须用#pragma NOAREGS关闭掉绝对寄存器访问,如果中断里非要调用函数,连同函数也要放在#pragma NOAREGS的作用域内.如例所示:#pragma SAVE#pragma NOAREGS 是说,如果你在不用中断时任务栈深定为8的话,现在就要定为8+4 = 12了.另外说句废话,中断里处理的事一定要少,做个标记就行了,剩下的事交给对应的任务去处理.现在小结一下:切换任务时要保证没有寄存器跨越任务,否则产生任务间寄存器覆盖. 使用静态变量解决切换任务时要保证没有变量跨越任务,否则产生任务间地址空间(变量)覆盖. 使用静态变量解决两个不同的任务不要调用同时调用同一个函数,否则产生重入覆盖. 使用重入申明解决。
什么是多任务处理?
什么是多任务处理?多任务处理是计算机科学领域的一个重要概念,它指的是在同一时间内处理多个任务的能力。
在现代计算机系统中,多任务处理是一项基本功能,它使得计算机可以同时执行多个程序,从而提高系统的效率和性能。
多任务处理技术是操作系统的核心组成部分,它允许用户在同一时间内进行多项工作,如同时打开多个应用程序、在浏览器中浏览网页、听音乐等等。
下面将依次介绍多任务处理的相关概念、分类和优势。
一、多任务处理的概念多任务处理是指计算机系统能够同时执行多个任务的能力。
在实际应用中,这些任务可以包括用户的各种操作、程序的运行以及系统的管理等。
通过多任务处理,计算机能够快速响应用户的操作并保持高效运行。
二、多任务处理的分类1. 抢占式多任务处理抢占式多任务处理是一种操作系统调度方式,它会根据一定的策略自动中断当前执行的任务,并切换到下一个待执行的任务。
这种方式可以保证任务的快速响应和高效运行。
2. 协作式多任务处理协作式多任务处理是指任务的切换需要由任务自身配合完成,而不是由操作系统主动调度。
任务在执行完成后主动将控制权交给其他任务,这种方式适用于处理简单任务和资源受限的环境。
三、多任务处理的优势1. 提高系统的效率和性能多任务处理能够充分利用计算机系统的资源,将多个任务分配到不同的处理器或者时间片中执行,从而提高系统的效率和性能。
2. 增加用户的工作效率多任务处理允许用户同时进行多项工作,如在处理文档的同时查看邮件,这样可以提高用户的工作效率,节省时间和精力。
3. 提高系统的稳定性和可靠性通过多任务处理,计算机系统可以更加灵活地分配资源和处理任务,当一个任务出现异常时,其他任务仍然可以正常运行,从而提高了系统的稳定性和可靠性。
尽管多任务处理技术带来了很多好处,但也存在一些挑战和限制。
例如,在多任务处理过程中,不同任务之间的资源竞争可能导致系统性能下降,需要合理分配和管理系统资源。
此外,任务的调度和切换也需要一定的时间和开销,可能会影响系统的响应速度。
操作系统的多任务处理
操作系统的多任务处理多任务处理是现代操作系统的重要特性之一,它使得计算机可以同时执行多个任务。
通过合理的任务切换和资源分配,操作系统能够提高计算机的利用率和效率。
本文将介绍操作系统的多任务处理原理、策略和应用。
一、多任务处理原理多任务处理是指在一个计算机系统中同时执行多个任务的能力。
操作系统通过任务调度算法和进程管理来实现多任务处理。
在单核处理器系统中,操作系统通过时间片轮转等算法按照一定的时间片轮询切换任务,使得任务在人眼看来是同时执行的。
而在多核处理器系统中,操作系统可以将多个任务分配给多个处理器核心并行执行。
二、多任务处理策略1. 抢占式调度抢占式调度是指操作系统可以随时中断正在执行的任务,将处理器分配给其他高优先级任务的调度策略。
当有更高优先级的任务就绪时,操作系统可以立即切换到该任务并执行,以保证高优先级任务的及时响应。
2. 合作式调度合作式调度是指任务执行必须主动释放处理器资源,才能让其他任务执行的调度策略。
在合作式调度中,每个任务都需要遵守一定的规则,如不可长时间占用处理器资源,否则会影响其他任务的执行。
3. 多级反馈队列调度多级反馈队列调度算法将任务分为多个优先级队列,每个队列具有不同的时间片大小。
当任务执行完成后,如果没有新任务到达,则继续执行该队列的下一个任务;如果有新任务到达,则将该任务插入到更高优先级队列中。
这种调度策略既能保证高优先级任务优先执行,又能公平地分配处理器资源。
三、多任务处理的应用1. 多媒体播放操作系统的多任务处理能力使得计算机可以同时播放多个媒体文件,如音频、视频等。
用户可以在观看视频的同时听音乐,提高了用户体验。
2. 并行计算通过多任务处理和多核处理器,操作系统可以将大型计算任务分解为多个子任务,然后利用不同核心并行执行这些任务,提高计算速度和效率。
3. 虚拟化技术多任务处理为虚拟化技术的实现提供了基础。
操作系统可以将物理资源虚拟化为多个虚拟机,每个虚拟机可以独立运行不同的任务,实现资源的有效利用和管理。
简单协作式多任务操作系统设计
简单协作式多任务操作系统设计引言根据老师的要求,在这次的课程设计实验中,我们需要设计出满足老师要求的简单协作多任务操作系统。
本次实验中,我们需要设计的是简单协作式多任务操作系统,uC/OS-II是规模最小的系统之一,它的实质是一个微型内核,它是基于优先级的可剥夺型内核,系统中的所有的任务都是一个唯一的优先级别,它适合应用在实时性要求强的场合。
根据我们自身的能力和实际情况,我们发现,我们可以采用C语言来编写我们所需要的程序。
uC/OS-II的一个特点是它区分用户空间和系统空间,所以,它适合应用在比较简单的处理器上,所以,我们能够在自己的电脑上进行程序编写。
在uC/OS-II里,每个任务都有一个任务控制块,这是一个比较复杂的数据结构;任务控制块(即TCB)在uC/OS-II里是用来记录任务堆栈指针、任务的当前状态、任务的优先级别等一些与任务管理有关的属性的表。
而在uC/OS-II 中,会用两条链表来管理TCB:一条是空任务块链表(没有分配任务),一条是任务块链表(其中所有任务控制块已经分配给任务)。
同时,每个任务都有一个自身的堆栈,它们是独立属于任务本身的,即为自用堆栈。
堆栈的作用这里也简单的说明一下,即保护现场,传递函数,设置局部变量,保存任务上下文等。
通过指针的指向来控制堆栈的使用,先指向控制块,从栈中提取上下文,送入到CPU中运行,运行完毕后指针指向控制块提取下一个任务,依次循环执行。
任务上下文的切换实质是断点数据的切换,也就是处理器(CPU)的堆栈指针的切换,被中止的运行任务堆栈指针要保护到该任务的TCB中,待运行任务的任务堆栈指针要由该任务控制块转存到处理器的SP中。
uC/OS-II也提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等,由于我们这一次的实验要求比较低,所以,其中的函数调用我们是用不到的,具体使用的函数,我们会在下面的程序设计中进行说明。
操作系统的分类及特点分析
操作系统的分类及特点分析操作系统是计算机系统中重要的一部分,它负责管理计算机的硬件和软件资源,为用户和应用程序提供一个可靠、有效的工作环境。
根据其功能、性能和使用方式的不同,操作系统可以分为多种不同的分类。
本文将对操作系统的分类及特点进行分析。
一、基于功能的分类1. 批处理操作系统批处理操作系统主要用于处理大量作业的计算机系统。
它可以自动地按照用户事先设定的顺序和规则,无需人工干预就能够连续地执行多个作业。
这种操作系统具有高度的自动化和高效率的特点,因此适用于对处理速度要求较高的场合。
常见的批处理操作系统有IBM的OS/360和微软的MS-DOS等。
2. 分时操作系统分时操作系统允许多个用户通过终端同时使用计算机系统。
它通过将CPU的时间片划分为多个较短的时间段,每个用户轮流使用CPU,给人一种同时使用计算机的错觉。
这种操作系统可以有效地提高计算机的资源利用率,适用于多用户共享的大型计算机系统。
比较知名的分时操作系统有UNIX和Windows Server等。
3. 实时操作系统实时操作系统要求在规定的时间内完成任务的处理,对响应时间要求非常高。
它主要用于控制、通信、测量和嵌入式系统等领域,如航空航天、工业自动化、医疗设备等。
实时操作系统可以分为硬实时操作系统和软实时操作系统,前者的响应时间非常严格,而后者则具有更高的灵活性。
二、基于处理器的分类1. 单任务操作系统单任务操作系统只能一次执行一个任务。
它主要用于早期的个人计算机和嵌入式系统等,因为这些系统的资源较为有限,无法同时处理多个任务。
典型的单任务操作系统有DOS和嵌入式实时操作系统。
2. 多任务操作系统多任务操作系统能够同时执行多个任务,可以在同一时刻处理多个任务请求,提高了系统资源的利用率。
多任务操作系统可以分为抢占式多任务操作系统和协作式多任务操作系统。
前者通过强制性地暂停一个任务,并分配给其他紧急任务来提高系统的响应能力,而后者则依赖于任务间的协作来管理和分配资源。
实用多任务操作系统(RTOS)进入单片机开发领域
实用多任务操作系统(RTOS)进入单片机开发领域
肖海桥
【期刊名称】《今日电子》
【年(卷),期】1999(000)002
【摘要】1.实时多任务操作系统(RTOS) 更加面向硬件系统,而不是操作者嵌入式系统处理器一般都是独立工作的,没有人的直接参与;即使参与,也没有大量的文字信息输出,这是和桌面计算机有所不同的。
因此RTOS着重面向的是硬件。
【总页数】3页(P8-10)
【作者】肖海桥
【作者单位】中国单片机公共实验室
【正文语种】中文
【中图分类】TP316.2
【相关文献】
1.新型实用透明陶瓷进入民用领域 [J],
2.实时多任务操作系统(RTOS)引入单片机开发领域的研究 [J], 户永清;罗江
3.一个实用的实时多任务操作系统 [J], 梁民基;梁炜伦
4.多色印刷技术已进入实用市场领域 [J],
5.实时多任务操作系统Z80-RTOS的设计 [J], 陈亨勋
因版权原因,仅展示原文概要,查看原文内容请购买。
操作系统的多任务处理和调度算法
操作系统的多任务处理和调度算法在计算机科学领域中,操作系统是一种重要的软件系统,它起着管理和控制计算机硬件和软件资源的作用。
其中,多任务处理和调度算法是操作系统的核心功能之一。
多任务处理指的是操作系统能够同时运行多个任务(程序),并在它们之间进行切换执行的能力。
这种并发执行的方式可以提高计算机系统的效率和资源利用率。
而调度算法则负责决定哪个任务应该优先执行、如何分配处理器时间以及如何调度进程等。
操作系统的多任务处理和调度算法的设计要考虑到以下几个方面:1. 执行顺序:操作系统需要确定多个任务执行的顺序。
常见的执行顺序有两种模式:抢占式和非抢占式。
在抢占式模式下,操作系统可以中断当前正在执行的任务,并将处理器资源分配给其他任务。
而在非抢占式模式下,任务只有在主动释放处理器资源后,才能将其分配给其他任务。
2. 优先级调度:每个任务都可以分配一个优先级,用于确定任务的执行顺序。
优先级调度算法可以根据任务的优先级来决定任务的执行顺序。
一般情况下,优先级较高的任务会先被执行,并占用更多的处理器时间。
3. 时间片分配:为了确保每个任务都能得到执行的机会,操作系统会将处理器的执行时间划分为若干个时间片段。
时间片分配算法可以决定每个任务获取的时间片的长度。
常见的时间片分配算法有固定时间片轮转算法和动态时间片轮转算法。
固定时间片轮转算法为每个任务分配相同长度的时间片,而动态时间片轮转算法则根据任务的优先级或执行时间来分配不同长度的时间片。
4. 阻塞和唤醒:在多任务处理中,任务可能会由于等待某些事件的发生而阻塞。
当任务被阻塞时,操作系统需要将处理器资源分配给其他可执行的任务。
一旦被等待的事件发生,操作系统会将任务唤醒,并恢复其执行。
5. 死锁处理:在多任务处理中,由于各个任务之间资源的竞争,可能会出现死锁的情况。
死锁指的是多个任务因为相互等待对方释放资源而无法继续执行的状态。
操作系统需要设计相应的死锁检测和解除机制,以避免系统陷入死锁状态。
计算机操作系统中的多任务处理方法
计算机操作系统中的多任务处理方法随着科技的进步和计算机应用的普及,计算机操作系统的发展也变得日益重要。
操作系统是计算机系统中的核心软件,负责管理和控制计算机硬件资源,为用户和应用程序提供良好的使用界面和资源分配。
其中,多任务处理是操作系统的重要功能之一,它使得计算机可以同时运行多个任务,提高了整体的工作效率。
在计算机操作系统中,多任务处理分为两种基本方法:并行处理和时间片轮转。
首先,我们来讨论并行处理。
并行处理是指多个任务同时进行的处理方式。
在并行处理中,计算机系统会为每个任务分配独立的处理器,任务可以在不同的处理器上并行执行。
这样做的好处是可以充分利用计算机系统的处理能力,提高整体的处理速度和效率。
例如,在一个多核处理器系统中,可以同时运行多个不同的任务,这样可以确保每个任务都有足够的处理能力,不会因为其他任务的干扰而出现卡顿或延迟的情况。
并行处理还可以提高计算机系统的可靠性,当一个处理器出现故障时,其他处理器仍然可以继续工作,从而保障整个系统的正常运行。
然而,并行处理也存在一些问题。
首先是资源管理的复杂性。
由于每个任务都需要独立的处理器和内存空间,操作系统需要确保各个任务之间的资源互不冲突,同时还要合理地分配和调度各个任务所需的资源。
这对于操作系统来说是一个挑战,需要设计合适的调度算法和资源分配策略。
其次是任务之间的通信和同步问题。
在并行处理中,不同任务之间可能需要进行数据交换或共享资源,这就需要操作系统提供相应的机制来保证数据的一致性和并发性。
除了并行处理,操作系统还使用了时间片轮转的方式来实现多任务处理。
时间片轮转是指将处理器的使用时间划分为若干个时间片,每个任务依次在一个时间片内执行一段时间,然后切换到下一个任务。
时间片轮转可以保证每个任务都能得到一定的处理时间,从而避免了某个任务长时间占用处理器的情况。
虽然时间片轮转无法实现真正的并行处理,但却可以在多个任务之间实现快速的切换,从而让用户感觉到任务是同时执行的。
计算机操作系统中的多任务处理和调度
计算机操作系统中的多任务处理和调度计算机操作系统是现代计算机系统中的核心组成部分,负责管理计算机硬件和软件资源的分配和调度。
在操作系统中,多任务处理和调度是其中重要的概念和功能之一。
本文将围绕计算机操作系统中的多任务处理和调度展开论述,探讨其原理、分类、算法和应用。
一、多任务处理的原理多任务处理是指在计算机系统中同时运行多个任务的能力。
在计算机操作系统中,多任务处理通过时间片轮转和进程切换来实现。
操作系统将CPU的使用时间分割成若干个时间片,每个任务被分配到一个时间片内运行,当时间片用完后,操作系统将当前任务暂停并让下一个任务开始运行,通过不断切换执行的任务,实现多任务处理。
二、多任务处理的分类根据任务执行的方式和特点,多任务处理可分为协作式和抢占式两种。
1. 协作式多任务处理协作式多任务处理是指每个任务在执行过程中,主动释放CPU的控制权给其他任务。
任务间的切换由当前任务自行控制,任务的执行顺序和时间由任务本身协调。
然而,这种方式容易造成某个任务出现问题导致整个系统崩溃,同时也无法有效利用系统资源。
2. 抢占式多任务处理抢占式多任务处理是指操作系统根据一定的策略主动中断当前任务的执行,将CPU控制权转移到其他任务上。
任务的执行顺序由操作系统决定,可以根据任务的优先级和时间片等进行调度。
这种方式可以更好地保障系统的稳定性,提高系统资源的利用率。
三、多任务处理的调度算法在实现多任务处理中,操作系统需要根据任务的优先级和运行状态进行调度,以保证每个任务都能得到合理的执行机会。
常见的调度算法包括:1. 先来先服务调度(FCFS)先来先服务调度是指按照任务到达的先后顺序进行调度。
优点是简单易懂,缺点是无法有效地处理长任务和短任务混合的情况,可能导致长任务占用CPU时间较长,影响其他任务的执行效率。
2. 短作业优先调度(SJF)短作业优先调度是指按照任务执行时间的长短进行调度,即优先执行执行时间短的任务。
电脑操作系统的多任务处理能力与效率
电脑操作系统的多任务处理能力与效率随着电脑系统技术的不断发展,人们对于电脑的使用需求也在不断提高。
在电脑的使用过程中,我们经常需要同时进行多项任务,如同时听音乐和编写文档、同时进行浏览网页和下载文件等。
在这种情况下,电脑操作系统的多任务处理能力和效率显得尤为重要。
本文将从多任务处理的概念、多任务处理的方式以及提高多任务处理效率三个方面进行探讨。
一、多任务处理的概念所谓多任务处理,指的是一台计算机同时运行两个或两个以上的程序,而这些程序似乎同时运行,但实际上是通过计算机操作系统的时间片轮转算法,在极短的时间内交替进行。
在多任务处理的过程中,计算机系统将所有的任务进行分配和调度,使得各个任务能够在一个时间段内同时进行,从而实现“同时运行”的效果。
二、多任务处理的方式目前,常用的多任务处理方式主要有两种:1.协作式多任务处理协作式多任务处理是指多个程序需互相配合,并通过各自提供的接口完成任务的分割与调度。
在这种处理方式下,每个程序都需时刻关注所有进程的运行情况,并在相应的操作系统提供接口的时间内释放CPU资源,以便其他程序能够执行。
这种处理方式需要程序之间的协调配合,如果一个程序出错或出现卡顿现象,会影响所有程序的运行效率。
2.抢占式多任务处理抢占式多任务处理是指操作系统可以自动在不同的进程中进行切换,并以时间片划分的方式进行运行。
在这种方式下,某个程序可以在其运行时自动被挂起,而另一个程序运行。
这种处理方式实现起来比较简单,但是可能会影响某些程序的响应速度。
三、提高多任务处理效率的方法1.增加CPU和内存CPU和内存是多任务处理所需的两大硬件资源。
增加CPU和内存可以提高系统的运行速度,从而提高多任务处理的效率。
在购买计算机时,可以根据自己的需求选择不同配置的CPU和内存,以达到最佳的运行效果。
2.优化系统设置优化系统设置可以减少运行时占用CPU和内存的程序和服务。
关闭不必要的自启动程序和服务,可以释放CPU和内存资源,提高系统运行速度。
操作系统多任务处理
操作系统多任务处理多任务处理是操作系统中的一项重要功能,它允许多个任务同时存在于计算机系统中,并能够合理地分配资源和时间片,以便它们能够高效地进行工作。
本文将探讨多任务处理的基本概念、调度算法以及与之相关的一些问题和挑战。
一、多任务处理的基本概念多任务处理是指操作系统能够在同一台计算机上同时执行多个任务。
这些任务可以是用户应用程序,也可以是系统进程。
多任务处理的基本概念包括进程、线程和调度。
1. 进程:进程是计算机执行任务的基本单位,每个进程都拥有独立的虚拟地址空间和资源,它们之间相互隔离,互不干扰。
操作系统通过进程控制块(PCB)来管理和调度进程。
2. 线程:线程是进程的执行单元,一个进程可以包含多个线程,它们共享相同的虚拟地址空间和其他资源。
线程之间的切换比进程之间的切换更轻量级。
3. 调度:调度是指操作系统为任务分配处理器时间的过程,目的是使得任务能够公平地获得处理机资源,并能够按照一定的优先级和策略来执行。
二、多任务处理的调度算法操作系统使用调度算法来决定任务的执行顺序和时间片分配。
常见的调度算法包括先来先服务(FCFS)、最短作业优先(SJF)、时间片轮转(RR)等。
1. 先来先服务(FCFS):该算法按照任务的到达顺序进行调度,先到达的任务先执行,无法提前中断任务。
这种算法适用于长任务和对响应时间要求不高的场景。
2. 最短作业优先(SJF):该算法按照任务执行时间的长短进行调度,执行时间短的任务优先执行,可以减少平均等待时间。
但是,无法预测任务的执行时间,因此可能导致长任务的等待时间过长。
3. 时间片轮转(RR):该算法将处理机时间划分为固定大小的时间片,每个任务按照轮转方式进行调度。
时间片到期后,任务需要放弃处理器并进入就绪队列。
时间片轮转算法能够确保所有任务得到公平的执行时间,但是对于长任务可能会导致响应时间过长。
三、多任务处理的问题和挑战多任务处理虽然提高了计算机系统的效率和资源利用率,但也面临一些问题和挑战。
计算机软件多任务处理与切换技巧
计算机软件多任务处理与切换技巧一、多任务处理的概念及分类多任务处理是指计算机能够同时执行多个任务,提高系统资源利用率和系统响应速度的能力。
根据任务之间的关系,多任务处理可以分为协作式多任务处理和抢占式多任务处理。
1.协作式多任务处理协作式多任务处理是指任务按照一个约定俗成的规则主动地释放CPU资源给其他任务使用。
这种方式需要任务之间相互配合,一旦有任务出现故障或不合理占用资源,就可能导致整个系统崩溃。
2.抢占式多任务处理抢占式多任务处理是指操作系统根据任务的优先级和时间片轮转算法,主动剥夺当前任务的CPU资源,分配给其他优先级更高的任务。
这种方式能够保证高优先级任务及时得到执行,并且不会让低优先级任务长时间等待。
二、多任务处理的应用场景多任务处理广泛应用于各个领域的计算机系统,特别是需要同时处理多个任务的实时系统、服务器和操作系统等。
1.实时系统实时系统需要对多个任务进行实时调度和处理,比如工业自动化控制系统、交通信号灯控制系统和军事指挥系统。
这些系统需要在严格的时间限制下对任务进行调度,保证任务能够在规定的时间内完成。
2.服务器服务器通常需要同时处理多个用户请求,比如Web服务器和数据库服务器。
通过多任务处理,服务器可以并行处理多个请求,提高系统的吞吐量和响应速度。
3.操作系统操作系统本身就是一个需要同时处理多个任务的系统,通过多任务处理,可以保证系统能够同时运行多个应用程序,并且能够及时响应用户的操作。
三、多任务处理的切换技巧多任务处理过程中,任务之间的切换是非常关键的环节,合理的切换技巧可以提高系统的响应速度和资源利用率。
1.时间片轮转时间片轮转是一种经典的多任务调度算法,即将系统的CPU时间划分为固定大小的时间片,每个任务在一个时间片内得到执行,时间片用完后,系统切换到下一个任务执行。
这种方式能够保证每个任务得到执行,并且公平地分配CPU资源。
2.优先级调度优先级调度是根据任务的优先级来决定任务的执行顺序。
操作系统的多任务处理与并发控制
操作系统的多任务处理与并发控制操作系统是计算机系统中最基础、最核心的软件之一。
它承担着管理和控制计算机硬件资源的重要任务,其中包括多任务处理和并发控制。
本文将探讨操作系统在实现多任务处理和并发控制方面的原理与方法。
一、多任务处理多任务处理是指操作系统能够同时处理多个任务,使得用户可以同时运行多个应用程序,并在它们之间切换而不会出现冲突或中断。
实现多任务处理的核心概念是进程和线程。
1. 进程进程是操作系统中的基本执行单位,每个进程都有自己的地址空间、数据和代码。
操作系统通过进程调度算法来决定执行哪个进程以及如何切换进程。
进程之间的切换由操作系统负责,这种切换称为上下文切换。
操作系统通过保存和恢复进程的上下文信息来实现上下文切换,包括进程的寄存器、程序计数器和栈指针等。
2. 线程线程是进程中的一个执行流程,一个进程可以包含多个线程,这些线程共享进程的资源。
与进程不同,线程的调度和切换更加高效,开销更小。
在单核处理器上,线程的并发执行是通过操作系统的时间片轮转来实现的,而在多核处理器上,每个线程可以被分配到不同的核上并行执行。
二、并发控制并发控制是指操作系统处理多个用户或进程对共享资源的访问问题。
在多任务处理的环境下,多个进程或线程可能同时访问共享资源,若不加以控制,就会出现数据竞争、死锁等问题。
因此,操作系统需要提供并发控制机制来保证共享资源的正确访问。
1. 临界区临界区是指一段代码,在某一时刻只能有一个进程或线程访问。
为了保证共享资源的一致性,操作系统要求进程在进入临界区之前先获取互斥锁,进入后执行临界区代码,然后释放互斥锁让其他进程进入。
互斥锁是一种同步机制,用于协调对共享资源的访问。
2. 信号量信号量是一种用于进程间通信和同步的机制。
操作系统通过信号量来控制对共享资源的访问。
信号量有两种类型:二进制信号量和计数信号量。
二进制信号量只能取两个值(0或1),用于互斥访问共享资源。
计数信号量可以取多个值,用于控制同一时刻可访问某一共享资源的进程或线程数量。
操作系统的高级功能介绍与操作教程
操作系统的高级功能介绍与操作教程第一章:多任务处理在操作系统中,多任务处理是一种重要的功能,它允许多个程序同时在计算机上运行。
这种能力可以提高计算机的效率和响应时间。
多任务处理可以分为两种类型:协作式和抢占式。
1.1 协作式多任务处理在协作式多任务处理系统中,每个程序都需要自觉地释放CPU 的控制权给其他程序。
操作系统并不会干预程序的执行顺序。
这种方式可以保证程序之间的相互配合,但是如果一个程序出现问题,可能会导致整个系统崩溃。
1.2 抢占式多任务处理在抢占式多任务处理系统中,操作系统具有中断程序执行的权限,可以强制终止一个程序并切换到其他程序。
这种方式可以更好地保证系统的稳定性,但是可能会导致某些程序执行的延迟。
第二章:虚拟内存管理虚拟内存是指操作系统给每个程序提供的一个与物理内存相对应的虚拟地址空间。
这样,每个程序都可以认为自己独占整个内存空间,而不受其他程序的影响。
虚拟内存管理可以提升系统的运行效率和应用程序的执行速度。
2.1 分页式内存管理操作系统将物理内存划分为固定大小的页框,每个页框与虚拟内存中的一页对应。
当程序访问某个虚拟地址时,操作系统会将该地址转换为物理地址并将相应的页框加载到内存中。
2.2 分段式内存管理操作系统将整个虚拟内存划分为多个段,每个段对应一个独立的功能模块或变量。
这样,程序可以按照不同的段进行访问,提高了代码的可维护性和可重用性。
第三章:文件系统管理文件系统管理是操作系统中非常重要的一个功能,它负责管理计算机中的文件和目录。
文件系统管理可以使用户方便地存储、访问和管理文件,提高了整个系统的效率和可靠性。
3.1 文件结构文件结构决定了文件所占用的存储空间以及文件的组织方式。
常见的文件结构有顺序文件、索引文件和哈希文件等。
不同的结构适用于不同类型的应用程序。
3.2 文件访问权限文件访问权限是操作系统对文件的一种保护机制,它可以限制用户对文件的读、写和执行权限。
通过设置不同的权限,可以保护文件的安全性,防止未经授权的访问。
多任务操作系统的程序设计
多任务操作系统的程序设计多任务操作系统允许多个程序同时运行在同一台计算机上。
为了实现多任务,操作系统需要将CPU时间分配给各个程序。
多任务操作系统的程序设计主要有以下几个方面:●进程管理:进程是操作系统的基本抽象概念,它表示正在运行的程序。
进程管理是多任务操作系统的核心,它负责创建、调度、终止进程。
●资源管理:资源是程序运行所需的各种资源,包括CPU、内存、I/O设备等。
资源管理是多任务操作系统的重要功能,它负责分配和回收资源。
●通信:程序之间需要进行通信才能协调工作。
通信是多任务操作系统的重要功能,它负责提供程序之间的通信机制。
以下是多任务操作系统程序设计的具体要点:进程管理:1.进程创建:程序启动时,操作系统会创建一个进程来运行该程序。
2.进程调度:操作系统会根据一定的调度算法,将CPU时间分配给各个进程。
3.进程终止:程序结束时,操作系统会终止该程序的进程。
资源管理:1.资源分配:操作系统会根据进程的需要,分配给进程相应的资源。
2.资源回收:进程结束时,操作系统会回收进程使用的资源。
3.通信:4.进程间通信:操作系统会提供进程之间通信的机制,例如消息传递、共享内存等。
多任务操作系统的程序设计是一个复杂的过程,需要考虑多方面的因素。
在设计多任务程序时,需要遵循以下原则:●程序设计的并发性:多任务程序需要具有并发性,才能在同一台计算机上同时运行多个程序。
●程序设计的安全性:多任务程序需要具有安全性,才能防止多个程序相互干扰。
●程序设计的效率:多任务程序需要具有效率,才能充分利用计算机的资源。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
TinyOS51
tnOsTaskCreate() 任务创建
tnOsSched() tnOsTaskDel() tnOsTaskCreate() tnOsStart() tnOsInit()
作用 初始化TinyOS51 实现任务切换, 创建一个任务, 启动多任务环境, 删除一个任务, 执行下一个任务 内部变量 为任务创建资源 并执行第一个任 释放删除任务的 (内存) 务 资源(内存) 注意 调用此函数之前 必须在任务中调 该函数可在调用 必须在调用其他 可以删除其他任 tnOsInit()函数后 务或自身 用,而且每个任 函数之前调用, 必须创建至少一 并只能调用一次 务都必须周期性 任意时候调用, 个任务,该函数 地调用它 但在调用 不能重复调用, tnOsStart()函数之 且不返回 前必须调用一次
启动多任务环境
在操作系统中,运行第一个用户任务的过程被称为“启动多任务环境”,也 即运行句柄为0的任务。TinyOS51是通过调用tnOsStart()函数实现的,在内部由 longjmp()函数执行句柄为0的任务。
void tnOsStart (void) { longjmp(__GtcbTasks[0].jbTaskContext); }
tnOsTaskDel() 任务删除
名 称 名 称 函数原型 名 称 函数原型 函数原型 输入参数 输入参数 返回值
tnOsSched() 任务切换
tnOsTaskCreate() tnOsTaskDel() TN_OS_TASK_HANDLE tnOsTaskCreate (void( * tnOsSched() tnOsStart() pfuncTask)(void), idata unsigned char *pucStk) tnOsInit() void tnOsTaskDel (TN_OS_TASK_HANDLE thTask)
设置任务就绪状态 创建任务中不需要创建空闲任务。
创建任务失败
结束
创建任务时TCB填充
假设系统最大的任务数为2,系统刚完成初始化,现在要创建Task0,TCB的申请和 填充过程是:
TCB0 bp SP addr15~addr8 addr7~addr0 ucTaskStat xxxxxxxx bp pucStkTask0 xxxxxxxx xxxxxxxx Task0Addr高8位 xxxxxxxx Task0Addr低8位 00000001 00000000 TCB1 xxxxxxxx xxxxxxxx xxxxxxx xxxxxxxx 00000000
任务块结构体 __GtcbTasks[x]
上下文信息 jbTaskContext
上下文信息 jmp_buf [x] bp SP addr15~addr8
ucTaskStat 任务状态字
addr7~addr0
ucTaskStat 即任务状态字,是char型变量,对于V1.0版本的 TinyOS51,它有两个值,如下宏定义所示: #define __TN_TASK_FLG_DEL 0x00 // 任务被删除 #define __TN_TASK_FLG_RDY 0x01 // 任务就绪
TinyOS51
支持的任务 对最大任务数目 支持的任务数量有限 数量无限 没有限制
51单片机idata空间256字节 0xFF
Task3任务堆栈 Task2任务堆栈 Task1任务堆栈
Task0任务堆栈
1 2
单片机片内idata空间有限 任务堆栈占用idata资源
不能继续分配 任务堆栈 0x00
TinyOS51支持的任务数
RTOS使用TCB指针识别任务,对于 SDCC编译器来说,指针变量占3个字节空 间,当任务较多时,内存开销大。
pTCB2 … pTCBn
TCB2 …
TCBn
TinyOS51
TCB[1]
TinyOS51使用TCB结构体类型数组 下标来识别任务,类型是无符号char型, 仅占一个字节内存空间,适合内存资源 紧张的51单片机
调用 setjmp()
是否存在下一 个就绪任务? 是
否
实现系统 空闲任务
通过任务转换实现
保存当前任务TCB
等待当前任务就绪
调用 longjmp()
从下一个就绪任务 TCB恢复上下文
结束
任务切换程序说明1
tiny_os_51_core.c
Task0程序 入口地址
tnOsCreate(Task0Addr, pucStkTask0)
CPU
TCB填充:包括bp, TCB申请:由于 TCB0的状态字为 堆栈指针,函数入 口地址以及设置任 “删除状态”,所 有将其分配给Task0 务为“就绪状态”
Task0堆栈指针
创建任务的程序实现
tiny_os_51_core.c
thTask;
// 操作的任务
for (thTask = 0; thTask < TN_OS_MAX_TASKS; thTask++) { __GtcbTasks[thTask].ucTaskStat = TN_TASK_FLG_DEL; } __GthTaskCur } = 0; // 初始化任务号为0 // 使任务处于删除状态
0xFF
Task3任务堆栈 Task2任务堆栈 Task1任务堆栈 Task0任务堆栈
0xFF
Task7任务堆栈
0x00
至少32字节空间 • R0~R7 • 编译器用到的空间 • TinyOS51占用空间 • 用户代码占用空间 0x00
Task1任务堆栈 Task0任务堆栈
实际最大任务数以3~4个最为合适
企业员工
操作系统任务
TCB全称是Task Control Block,即任务控制块,它是一个结构体数据结构, 用于记录各个任务的各种信息,包括任务堆栈指针、任务状态、延时计数、 等待事件指针等。
RTOS和TinyOS51任务识别
RTOS(实时操作系统)
指向TCB 的指针 pTCB1 TCB 结构体变量 TCB1
5.3 协作式多任务操作系统
目
录
整体规划 任务控制块 内核API 小结
简介
任务切换的时机完全取决于正在运行的任务;
现代操作系统不再使用,适合于学习范例;
下面介绍 TinyOS51 V1.0版 操作系统。
内核 API
TinyOS51 V1.0是完全用C语言编写的协作式多任务操作系统,它的内核API (Application Interface Program:接口函数)包括5个函数。 tnOsInit() 系统初始化 tnOsStart() 系统启动
TCB的程序实现
tiny_os_51_core.c
#define __TN_TASK_FLG_DEL #define __TN_TASK_FLG_RDY struct tn_os_tcb { jmp_buf jbTaskContext; // 用于存储上下文信息 // 任务状态字 0x00 0x01 // 任务被删除 // 任务就绪
TCB[2] … TCB[n] TCB结构体 类型数组 数组下标, 俗称句柄 Handle
TCB的构成
TCB的类型是结构体,对于V1.0版本的TinyOS51操作系统,它主要由上下文信 息和任务状态字组成。
jbTaskContext是 jmp_buf 数组类 型变量,而 jmp_buf 数组类 型在<setjmp.h> 中定义
TCB1
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 00000000
TCBn
xxxxxxxx xxxxxxxx
……
xxxxxxx xxxxxxxx xxxxxxxx 00000000
#define __TN_TASK_FLG_DEL #define __TN_TASK_FLG_RDY
TN_OS_TASK_HANDLE tnOsTaskCreate (void (*pfuncTask)(void), idata unsigned char *pucStk) { TN_OS_TASK_HANDLE thRt; for (thRt = 0; thRt < TN_OS_MAX_TASKS; thRt++) { // 搜索是否有空闲的任务控制块 if (__GtcbTask[thRt].uctaskStat == __TN_TASK_FLG_DEL) { setTaskJmp(pfuncTask, pucStk, __GtcbTasks[thRt].jbTaskContext); // 保存上下文信息 __GtcbTasks[thRt].ucTaskStat = __TN_TASK_FLG_RDY; // 任务就绪 return thRt; } } return -1; } // 如果创建任务失败,则返回-1 // 如果创建任务成功,则返回该任务的句柄
创建任务
在操作系统中,任何一个程序只有成为了任务才能在内存中运行,使用创建任务 创建与之对应的任务。
开始
如果没有空闲TCB 则不能创建任务
关于空闲任务
是否有空闲 任务控制块
是 保存上下文信息
否
空闲任务是CPU闲置时候运行的任务,TinyOS51为了节省片内 填充TCB的过程 RAM,决定使用调度器来完成空闲任务的工作。
// 执行0号任务
SP
PC 高8位
Task0 堆栈指针 Task0 入口地址高8位 Task0 入口地址低8位 CPU
Task0
Task1
低8位
Task2
任务切换
任务切换是指保存当前任务的上下文,并恢复需要执行任务的上下文的过程。
任务在宏观上是并发运行的
开始
task0