• Articles
  • How to develop an operating system using
Published by
Sep 20, 2013 (last update: Sep 22, 2013)

How to develop an operating system using C++?

Score: 3.9/5 (690 votes)
*****

how operating systems are developed?

using C(or actually) C++, assembly and any programming languages like ada, fortran, pascal you can develop your own operating system
but you have to use assembly in some places

an intruduction to assembly language

assembly is a low-level programming language that you can use it to control everything like adding something to CPU registers, control the memory and much more

how can i start to develop an operating system?

firstly, you have to know everything of your programming language like pointers, functions (here i want to use C++)
secondly, you must have some knowledge of the assembly language

what tools i need to develop an operating system?

to develop an operating system you must have these:
1. an assembler
assembler takes your assembly code and give's you the low-level outputs like an object containing your controls on CPU registers
the assembler here i want to use is nasm(netwide assembler)
you can download it from http://nasm.us
2. a cross-compiler
in order to develop an operating system, you must have a cross compiler because you must compile your kernel for it's executable format
here i use gcc(gnu compiler collection)
you can download it from http://gcc.gnu.org/
3. a linker
the linker take's your objects and link them to getter
here i use gnu binutils
you can download it from http://gnu.org/software/binutils
4. a virtual machine
in order to test your operating system, you must have a vm(virtual machine)
but it isn't necessary
here i use virtualbox
you can download it from http://virtualbox.org/

notes before you get started

1. in developing an operating system, you cannot and cannot and cannot use <iostream>, <fstream>, <memory>, <cstdio>, <cstdlib>, <windows.h>, <unistd.h> and all the platform API's
you must create all of them yourself
2. you must very very be careful
when you are developing, you have control of everything
so, you can destroy one or some or all of your hardwares
in this case, i recommend to use a virtual machine to test your operating system instead of rebooting more and more times

the bootloader

a bootloader is a bunch of code that is written in assembly language and must be 512 byets(1 sector)
it load's your operating system's kernel
we skip this section and use grub as are bootloader
you can download grub source code from http://gnu.org/software/grub
or actually you maybe want a compiled for floppy: searching in google will help you

a simple kernel

we want to develop an operating system
so, we have to create the functions ourselves
firstly, we create a file boot.asm with these contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
;boot.asm:the bootloader to boot are operating system with grub
[bits 32] ;we are in 32 bit
global start ;start's the operating system:we call it in the linker script
extern _kernel_main ;this is in are .cpp file and it is the main function of are kernel
;do not modify these lines(these are needed by grub)!
section .mbHeader

align 0x4
 
; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ    0x1BADB002           ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum required
 
MultiBootHeader:
   dd MAGIC
   dd FLAGS
   dd CHECKSUM
 
;you can modify these
start:
push ebx ;this is optional and load's the grub structure
call _kernel_main

now create a file: kernel.cpp with these contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stddef.h> //we can use it: it doesnt use any platform-related api functions
#include <stdint.h> //include it to get int16_t and some integer types

/* Hardware text mode color constants. */
enum vga_color
{
	COLOR_BLACK = 0,
	COLOR_BLUE = 1,
	COLOR_GREEN = 2,
	COLOR_CYAN = 3,
	COLOR_RED = 4,
	COLOR_MAGENTA = 5,
	COLOR_BROWN = 6,
	COLOR_LIGHT_GREY = 7,
	COLOR_DARK_GREY = 8,
	COLOR_LIGHT_BLUE = 9,
	COLOR_LIGHT_GREEN = 10,
	COLOR_LIGHT_CYAN = 11,
	COLOR_LIGHT_RED = 12,
	COLOR_LIGHT_MAGENTA = 13,
	COLOR_LIGHT_BROWN = 14,
	COLOR_WHITE = 15,
};
 
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
	return fg | bg << 4;
}
 
uint16_t make_vgaentry(char c, uint8_t color)
{
	uint16_t c16 = c;
	uint16_t color16 = color;
	return c16 | color16 << 8;
}
 
size_t strlen(const char* str)
{
	size_t ret = 0;
	while ( str[ret] != 0 )
		ret++;
	return ret;
}
 
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 24;
 
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;
 
void terminal_initialize()
{
	terminal_row = 0;
	terminal_column = 0;
	terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
	terminal_buffer = (uint16_t*) 0xB8000;
	for ( size_t y = 0; y < VGA_HEIGHT; y++ )
	{
		for ( size_t x = 0; x < VGA_WIDTH; x++ )
		{
			const size_t index = y * VGA_WIDTH + x;
			terminal_buffer[index] = make_vgaentry(' ', terminal_color);
		}
	}
}
 
void terminal_setcolor(uint8_t color)
{
	terminal_color = color;
}
 
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
	const size_t index = y * VGA_WIDTH + x;
	terminal_buffer[index] = make_vgaentry(c, color);
}
 
void terminal_putchar(char c)
{
	terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
	if ( ++terminal_column == VGA_WIDTH )
	{
		terminal_column = 0;
		if ( ++terminal_row == VGA_HEIGHT )
		{
			terminal_row = 0;
		}
	}
}
 
void terminal_writestring(const char* data)
{
	size_t datalen = strlen(data);
	for ( size_t i = 0; i < datalen; i++ )
		terminal_putchar(data[i]);
}
 
void kernel_main()
{
terminal_initialize();
terminal_writestring("wellcome to my first operating system!");
for(;;);
}

the linker script

create a file: linker.ld with these contents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* The bootloader will look at this image and start execution at the symbol
   designated as the entry point. */
ENTRY(start)

/* Tell where the various sections of the object files will be put in the final
   kernel image. */
SECTIONS
{
	/* Begin putting sections at 1 MiB, a conventional place for kernels to be
	   loaded at by the bootloader. */
	. = 1M;

	/* First put the multiboot header, as it is required to be put very early
	   early in the image or the bootloader won't recognize the file format.
	   Next we'll put the .text section. */
	.text BLOCK(4K) : ALIGN(4K)
	{
		*(.mbHeader)
		*(.text)
	}

	/* Read-only data. */
	.rodata BLOCK(4K) : ALIGN(4K)
	{
		*(.rodata)
	}

	/* Read-write data (initialized) */
	.data BLOCK(4K) : ALIGN(4K)
	{
		*(.data)
	}

	/* Read-write data (uninitialized) and stack */
	.bss BLOCK(4K) : ALIGN(4K)
	{
		*(COMMON)
		*(.bss)
	}

	/* The compiler may produce other sections, by default it will put them in
	   a segment with the same name. Simply add stuff here as needed. */
}

how to compile it

go to the shell (on windows cygwin is required):
type these commands:
1
2
3
nasm -f elf boot.asm -o boot.o
g++ -c kernel.cpp -o kernel.o -ffreestandinng -fno-exceptions -fno-rtti
gcc loader.o kernel.o -T linker.ld -o kern -nostdlib -nodefaultlibs -lgcc

kingratulations!
your first operating system has been compiled successfully!
now you can create an image using grub-mkrescue:
create a directory: iso
in that directory, create another directory: boot then in the boot directory, create a directory: grub and then create a file: grub.cfg with these contents(do not add the braces in a new line) in the grub directory:
1
2
3
menuentry "myOS" {
multiboot /boot/kern
}

then copy your kernel (kern) to iso/boot directory and run your shell again:
switch to the main directory of your kernel and type:
 
grub-mkrescue iso --output=kern.iso

now you can boot and enjoy from your first operating system:: this simple kernel without anything