Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Cooperative vs. Pre-emptive multitasking (bit of a rant) 1

Status
Not open for further replies.

AmkG

Programmer
May 12, 2001
366
PH
For those who don't know:
Cooperative multitasking-
Tasks are written as coroutines
(Tasks will periodiodically return control to the multitasker)
Pre-emptive multitasking-
Tasks are written as if they were single tasks; they are written in a straightforward manner.
Multitasker periodically interrupts each task, saves its state, and revives another task and its state.

Personally I find Cooperative multitasking to my taste - it's faster (there is no need to save states), uses lesser space (for the same reason) and it's harder to program for ( :) ). In fact Win16 programming used Cooperative multitasking; it is the newer Win32 which uses pre-emptive multitasking.

In fact I think the reason a lot of people are jumping into the pre-emptive bandwagon is simply to show off that they can make an interrupt routine which saves the state fast enough for pre-emptive multitasking to be feasible.

Everyone knows, of course, that pre-emptive multitasking is better. One thing a pre-emptive multitasker has which a cooperative multitasker doesn't is ALMOST COMPLETE control of the system. So if a task crashes, a pre-emptive multitasker will be able to remove the task from the system, while a cooperative multitasker can't. Or does everyone know that...?

I don't.

The AT has a 286 processor and while fast, was not fast enough to do pre-emptive multitasking in a graphics environment. On the AT architecture, there is in fact a way for a cooperative multitasker to detect if a task has crashed. I invite you all to think of just what trick it is for a cooperative multitasker to succesfully detect a crashed program.
"Information has a tendency to be free. Which means someone will always tell you something you don't want to know."
 
please give more information on how to make preemptive multitasking programs in asm. If you know some tutorials/links you have welcome. John Fill
1c.bmp


ivfmd@mail.md
 
Big BOSS AMKg,
Co-Operative Multi-tasking systems and Pre-emptive
systems..Good topic. But i fear u have understood it
wrong way..
Pre-emptive always mean that Kernel is Pre-emptive.
An user-level-task is ALWAYS pre-emptible. For exp, consider a UserTask like "while(1);" which will
never relinquish CPU, can be pre-empted after its
given time slice is expired. This is done even in
Co-Operative Multi-tasking approach.

Temme if I am wrong

Sarnath

Do not rejoice that ur code works.
it might be a special case of an error :-(
 
Anyway this is the reply to JohnFill:
I will be assuming that you intend to run in REAL mode. However, with some extension, you can implement the concepts here in Protected Mode.

The most important thing you will program in a pre-emptive multitasking system is the Task Switching Interrupt.

This interrupt essentially handles the tricky task of switching between two tasks. It needs access to a variable called "Current Task." The CurrentTask variable refers to the task which is currently being run by the microprocessor. The number it contains is used as an index to an array of task state structures. The Task State Structure must contain all the accessable microprocessor registers.

When the TaskSwitch interrupt is invoked (it is usually bound to the IRQ0 or timer interrupt), the first thing it does is save all the registers, including the CS:IP and Flags registers. Note that the task's CS:IP and Flags registers are pushed on its stack, so after saving the other registers it must pop them off the stack and save them. The registers are all saved in the tss indexed by the CurrentTask variable. (In psuedo-C, that would be tss[CurrentTask]. For example, tss[CurrentTask].ax=_AX;, etc.). Then, after saving the current task's registers (also known as its STATE), the TaskSwitch interrupt must then decide the next task to put into the CurrentTask variable. The easiest way is simply to increment the CurrentTask variable, then check if (CurrentTask>=NumTasks) {CurrentTask=0;} (this is still pseudo-C, C can't handle a TaskSwitch interrupt by itself). This is known as the Round Robin method since it switches tasks in a round-robin fashion (duh). After updating the CurrentTask variable, it will then retrieve each of the registers from the tss[CurrentTask]. It will now push the CS:IP and Flags registers onto the stack and issue an IRET. The Task Switch is then finished.

Note that I HIGHLY RECOMMEND that you capture the IRQ0 directly (at interrupt 08h) and not in the user hook (Int 1ch). The start of your TaskSwitch interrupt must call the previously installed interrupt handler BEFORE doing ANY processing. But this is of course at your own discretion.

Now to the complications. One thing you should never do is to interrupt the BIOS while it's doing something, such as reading data from the drive. So you need to hook into some other interrupts (such as BIOS's Int13h) in the following manner:
;note that these are in the CODE segment
oldInt13h dd ?
InBios db ?

newInt13h proc
inc InBios
pushf
Call dword ptr cs:[oldInt13h]
dec InBios
iret
newInt13h endp

Now your TaskSwitch routine must check InBios (and InDos, too, if you want) if it is set, and IRET immediately if it is. Note that you need to make a new int handler for each BIOS interrupt you want to defend against.

If your computer is slow, you may want to implement a counter. As is, the TaskSwitch routine will switch 18.2 times a second; on a slow computer each slice is too small to practically use. You may need to implement a counter and switch every 2 or 3 clock ticks.

Also, if each of your tasks will need to access files and talk to DOS, your TaskSwitch routine may want to check InDos also. If only one task will access files and talk to DOS, then you only need to trap the BIOS.

Another routine you need to make for your multitasker is the "Add Task" routine. A simple routine you can make is one which first sets a flag (such as your InBios flag) to show that it is in sensitive portion. The AddTask routine needs to know the address of the new task. The address of this new task is placed in the CS and IP portions of the last tss (which is usually addressed by tss[NumTasks]), then NumTasks is incremented. A default SS and SP are assigned to the task (each task must have its own stack). Then the sensitive flag is cleared and returned.

Another routine which is much more difficult is the "Kill Task" routine. Depending on your desired system, you may either need to move all the tss's back (which is easier on the AddTask routine but harder on the KillTask one), or just set a "used tss" flag (which is easier on the KillTask but harder on the AddTask and TaskSwitch), or not implement a Kill Task routine at all (instead killing all tasks when you kill the task switcher).

Initialization is simple. Set CurrentTask=0, NumTasks=1, then install the TaskSwitch interrupt into Int08h. Then you can invoke new tasks with the AddTask routine. To deinitialize, you only need to deinstall the TaskSwitch routine, although it is recommended that you kill each task beforehand.

If you need more detail, contact me at: alan@i-manila.com.ph

Also, Sarnath, I thought bringing up this topic in a "rant" format might encourage some people to put in their opinions about Cooperative and Pre-emptive multitasking. Think of it as a "survey." Personally I still think Cooperative is better, but can you change my mind about that? "Information has a tendency to be free. Which means someone will always tell you something you don't want to know."
 
Anyway, in WIN16 (especially Win 1.0) you can write while (1); and happily hang your system... until you press Ctrl-Alt-Delete.

What I'm ranting about are the CONCEPTS of them durned things. "Information has a tendency to be free. Which means someone will always tell you something you don't want to know."
 
Hey Amkg,
Doing some really good work.
Even i had done multi-programming in DOS..
ALT+TAB to switch execn between DOS executables.
What i would say is, there is a more elegant
way of multi-tasking -> STACK-SWITCHING.

Upon entry into the timer-handler, just save all
registers into stack and save <SS,SP> pair..
This SS,SP would now contain all registers, cs, ip,
and the stackBackTrace... ( I think u were missing
the stackBacktrace in ur logic.. But if u were saving
SP also inside ur regs, then fine..)

Now when u wanna give ctrl to this thread of execn,

Just make ss,sp = saved_ss,saved_sp and POP all
registers (in the order u have pushed) and just say IRET.
That would do..

DOesn't this sound elegant ?

If u want, u can take a small look at my page on
multi-threading in DOS.. ( in C )


if this doesn't work, use


I have also done multi-threading in USer-level in Linux.
Since kernel sources are available, this could b done
coooly. i m planning to post this in my site..

( Ohoh ! i can hear u shouting..&quot;Enough of ur rant&quot; :))

Also,
I never said that Pre-epmtive multi-tasking is better
than Co-operative..neither did i mean the vice-versa.
I just said u have understood the whole story wrongly.

Anyway, u r doing some real good work.
Try getting into Unix (or) how a PM OS work ?
That would b really fascinating for a guy like u.

Cheers,
Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
I don't know, maybe something simple like creating a really optimized OS for IBM PC compatibles. You know, one which takes advantage of quirks in the hardware. Say staggered disk accesses, staggered self- modifying code (&quot;staggered&quot; is my &quot;catchy&quot; term for first requesting to do something - which, say, modifies the code or starts the sector access(es) - then doing something unrelated, then calling the code or reading the sector).

Say, know where I can get some stuff on how to interface to a mouse? Hardware level. &quot;Information has a tendency to be free. Which means someone will always tell you something you don't want to know.&quot;
 
Hey AMKG,

I have also explored things a bit, and the real transition
was my entry into UNIX. Ofcourse, while in DOS, u have
everything in hand as an APP Programmer or SYstem
programmer. U would never like to work on UNIX where ur
hands are tied. But UNIX INTERNALS would work out
great things for u.
U r right about STAGGERING thing.
It happens in all contemporary OSes. They call it
Sleep, Wakeups. The Kernel puts a process into sleep,
whenever the service requested by the APP program needs
to wait for sthg. IN A NUTSHELL EVERYTHING IS INTERRUPT
DRIVEN (more precisely EVENT DRIVEN). This even happens
at the HW level. For exp, a SCSI device which is taking
part in a transaction may relinquish SCSI BUS OWNERSHIP
whenever it needs to do some slow mechanical work, like
positioning the read-write head. An IO is not carried out
as a single operation. There are lot of
disconnects - reconnects happening all the while.

About mouse,
I haven't worked much on mouse, except that
INT 0x33. I have done application level event-driven
programming with this mouse. But logically speaking,
since Mouse is connected via the SERIAL PORT, u need
to directly talk to the serial-port to which mouse is
connected. ( if u want to do what INT 0x33 does ..) . Apart
from that u need to know how to convert the &quot;Mickeys&quot;
to &quot;x,y&quot; stuff. i.e. a Mouse Driver's part.

Ok
ciao

Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
It's gonna be hard to try and depend on Int33h when you're writing you're own OS... because there isn't any Int33h.

Anyway what I'm thinking is to take advantage of the fact that even though we are multitasking, even though we try try try to give the illusion that we're doing more than one thread of execution, we're not really doing that. The PC has only one processor after all and it can process only one thread of execution at a time. I think that maybe, just maybe, all those things them complex modern OS's keep defending themselves against (deadlocks, starvations) can be avoided once we realize that we're not REALLY multitasking. How the heck can you deadlock a single task system, unless you're an idiot and make a subroutine stop to look for something another subroutine's got (which is why I'm thinking of going cooperative multitasking)?

More of a philosophical approach I mean. And more responsability for the Application (kinda got used under DOS where you have to take care of lotsa little details). Because if we actually dare to remove the OS's checking of deadlocks and stuff we're passing on that responsability to the Application Writers. And we can always make a different version for software developers which does indeed check for those things (and take up more processing time).
&quot;Information has a tendency to be free. Which means someone will always tell you something you don't want to know.&quot;
 
AMKG,
U r right that deadlock problems can occur
only in Multi-Processing systems.., where there are
multiple-processors running concurrently.

In a single-processor systems, INTERRUPTS are the
only problem. Hence UNIX type OSes have
sthg called Software Privilege levels for selectively
masking interrupts. ( the CRITICAL SECTION problem etc.)
I think INTEL offers this at the H/W level itself..
The local IOAPIC stuff..

What are u really upto..

An OS from the scratch ?
Will it run on real mode or PM ?
How about protection in ur OS ? I mean that
if u r in real mode, then u dont have it.

Cheers
Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
An OS STANDARD, not an OS per se.

Just something for wannabe OS programmers to try and implement.

What I'm thinking is, PM, Flat mode, NO PROTECTION. All tasks are at the same PLevel. Even system functions. And since we're doing Flat mode, we can get by with near calls. An advantage of this is that near calls are inherently relocatable, so our executables can be just plain images (no special headers)....

Problem is interfacing with system. Been thinking of using Int3 (Brkpoint). Do something like this:

Int 3
dw ModuleNum
dw ServiceNum

Which can be replaced with a 32-bit Near call. Int 3 handler intercepts, replaces it with a CALL near ptr ServiceProc.

There's also a Callup table for each app (or to be specific each app task) which is initialized by the app's initializator. Note that the initializator needs only to tell the system where it is. We do a message based system just like Win. Each message has a corresponding entry in the Callup table. When system needs to send a message (which are, incidentally, multiples of 4), it does:

mov esi,CallupOffset
add esi,message
call dword ptr [esi]

Each message handler is written as a subroutine.

What are the problems you can foresee here?

Wanna start a new thread instead? &quot;Information has a tendency to be free. Which means someone will always tell you something you don't want to know.&quot;
 
An OS standard ?!?!
Have u ever tried reading the POSIX ?

If u dont have protection in place, any APP program
can crash ur kernel. Whats the fun in doing such
a kernel ? I dont really understand that part. Is it
intended to be a RT-OS ?

I think probably u need to read a OS text book.
Jus browse thru the virtual memory sections and
u would understand why u need 2 modes of operation..
user n supervisor, whats an &quot;address space&quot; etc..

I m not trying to denigrate ur effort. Ur effort is
noble. but put it in the right direction. Thats more
important.

ok,
ciao
Cheers,
Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
Like I said, more responsability for an application. Must check pointers, must make sure that pointer maths won't crash into other people's areas.

And there's that unused fallback, the failsafe timer (timer 0 of second PIT). An app can't take control for more than a clock tick before it is crashed by the system, useful for a while(1) loop.

Like I was saying, maybe OS's are just too durned complex, maybe some of them things they're doing are unneeded, such as pampering an app, making sure they don't hurt each other. Take a look at DOS (maybe I'm just too addicted to the power DOS gives). More of an anarchic system. Each app must control itself, make sure it doesn't crash someone else.

Besides, protection can be added to the standard for the developer's version of the OS. Developer just has to make sure that the app &quot;passes&quot; the &quot;test&quot; before releasing. So why not add the protection to the release version of the OS? The protection still drains some of the processor time.

What's wrong with defining an OS Standard? An OS writer who writes an OS which doesn't follow a standard will be doing it only for his own amusement, who else will (or can) use it?

If RT-OS means something like a retro OS, yeah, maybe... &quot;Information has a tendency to be free. Which means someone will always tell you something you don't want to know.&quot;
 
Hey AMKG,
This is really interesting..
It is like &quot;Monkey -> Man&quot; and then &quot;Man -> Monkey&quot;.

Appln programs can't be expected to be innocent.
If u release an OS with appln having more responsibilities, i agree everything would be fast.. But how about security..
Lets say tomorrow u develop this OS as
a desktop stuff and market it to the world. Ur
competitors would easily bring down ur reputation
by writing number of viruses on it.. A single app
would do to crash ur kernel..
DOS is good for a start. i agree. U can
learn lot of things. And i know, u have learn a
hell lot of them. Thats 1 reason, why i also loved
DOS. U know 1 thing, for the 1st 2years after i bought
my PC. I worked only in DOS. I never booted thru WIndows.
For a programmer, DOS is heaven. But for a kernel programmer DOS is bad. Tell me one guy who is ready
to run a commercial web-server on DOS. Let him use
PM features to get more memory.. I bet none would
volunteer.
Inorder to address the problem of
&quot;an app program writing into another program's text n data&quot;,
the concept of Virtual Address Space was brought in..
Here, each program executes in its own virtual domain,
unaware of Physical Address Space. This way, an appln
program need not worry about the holes in its address
space and so many other things. Wondering what i am
bluffing ? Just follow the next para.
Let me tell u 1 thing, RAM doesn't occupy the entire physical address space i.e RAM address space and Physical address space are different.

I had a bad misconception about this untill very recently.
INTEL says, IO address space is different from Physical
address space. If thats the case, then all IO operations
should strictly done by &quot;IN&quot; and &quot;OUT&quot; instructions.
But while working in DOS, How are u able to populate,
video Memory with MOV instrn. I thought that &quot;VideoCard DMAs the B800:0000 from RAM to its internal buffers&quot;. But THIS IS NOT TRUE.
In an IBM-PC, the VideoCard buffers are memory
mapped.( for TEXT mode alone b800:0000 to ... ). So
RAM in B800:0 area is not at all used. Even if ur RAM
in this range is defective, u need not worry about it !
Thus the 1Mb address space available to DOS splits
as
1. 0-640k -> RAM
2. A000:0 - C000:0 -> Video Buffers.
3. C000:0 - FFFF:000F -> BIOS
( 640k = A000:0000, the graphics area starts here..)

The RAM in 0-640k are used. 640 to 1Mb of RAM is not
at all used. The Constants A000:0, C000:0 are all
dictated by great veterans, who thougt 640k is an overkill
for applns.. ( IBM people did that, i think ). Now, what
if a video-Card has 8Mb VRAM ? How would i MMAP it ?
So, video-card manufactures came up with BANK-SWITCHING.
This was an unwanted feature for video-cards. They
had to do that, coz DOS was riding high in the Market.
INTEL wanted to avoid all these NASTY things. Hence they
came up with something called PCI.
In PCI, peripheral devices can be negotiated for
address-space. The devices also have an Option to say
whether they are &quot;mmap&quot;ed or &quot;IO&quot;mapped devices.
Now 2 MB PCI mmaped video card can be Memory
Mapped for a continous 2Mb of Physical Address space.
The start of the physical address is negotiable with
the device.

Thus on a 32-bit machine, ( Physical Addres space is
4Gb), if u have 64Mb ,
U can partition ur Physical address space like this,
1. 0-64mB -> RAM
2. 64Mb - 66Mb -> Video Card.

What if u have 4G of RAM ?!?! Now this is going to b a
problem. There is no escape. u will need to lose Ph.RAM

The next natural qn is &quot;How to avoid BUS CONTENTION&quot;.

Therez sthg called &quot;RAM&quot; controller&quot; in ur PC,
to whom u can say &quot;RAM has to respond to this
set of {range of physical address} and shouldn't respond to
this set of {range of phy.add}. The RAM controller should
be programmed according to the system configuration u
would like to bring.
But PCI facility is not available in real mode.
All PCI bios code are 32-bit PM code.

BTB,
RT-OS means Real-Time OS, in which speed is critical
For exp, VxWorks. Here, there is no concept of address-
spaces. All guys are in kernel level.. But there is
a concept of kernel in the sense of a service-provider.
The kernel takes care of all scheduling, mem-allocn etc..
But applns, run in kernel mode by making use of these
facilities.

ok,
ciao

Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
Which brings us to the difference between an OS IMPLEMENTATION and OS STANDARD.

OS STANDARD says that there is NO PROTECTION. So Application writers (the legal type) want to be EXTRA CAREFUL with having to calculate addresses, and what not, because if they don't, no one would want to use their software because of potential crashes. And who says their software is good enough for the system? Well, that's where the concept of ISO comes in. Someone can run their software through an OS which really doesn't HAVE protection, to see if it works fine. When a company or what not gets ISO accreditation, they put it in as many ads as possible, and you can expect the App writers to do something similar with our version of accreditation.

At the same time, any OS IMPLEMENTATION wants to be as robust as possible. So the IMPLEMENTEER can put four layers of protection if he wants, provided that applications &quot;see&quot; the standard. It's just a matter of self-interest for the implementeer. Remember I am building a STANDARD here, not an implementation. YET.

I've always conceived of an OS as a giver of services, anyway, for both the program and the user. The kernel gives services to programs, while the programs bundled with the OS give services to the user. A simplistic view is the easiest for me.

The OS &quot;runs&quot; at the same level as the app, but not necessarily so (uuuuh). We can isolate an app's address space with a mini-kernel, the mini-kernel can pass control to the REAL kernel through a call gate if it needs to do something important, but not if the service needed is something like moving a piece of memory around, or an info piece. But that's an implementation detail already. That's ONE way of implementation. Others may exist.

Wanna continue over the mail? Address: alan@i-manila.com.ph
&quot;Information has a tendency to be free. Which means someone will always tell you something you don't want to know.&quot;
 
I honour ur views but
i dont agree with them.
Fine,
All the best for ur OS
adventures.
ciao

Sarnath
Do not rejoice that ur code works.
it might be a special case of an error :-(
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top