Kernel in C

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
PlayOS

Kernel in C

Post by PlayOS »

Hi,

If I want to create my kernel in C do I need to have some kind of memory manager in place? Also, is it possible to create global variables in C without a memory manager?
frank

Re:Kernel in C

Post by frank »

a lot of memory is empty at boot...
so you can just load your C kernel into 0x1000 :) (or somewere else)

yes, you can use global variables but you can't give them a value
:/
(gcc adds a lot of add al,[eax] or something (used disassembly)
don't know why it does...)
CoolnessItself

Re:Kernel in C

Post by CoolnessItself »

Is there a way around it?
Tom

Re:Kernel in C

Post by Tom »

I think I know what your talking about, tell me if i'm wrong....

like if you use "int someint = 20;"

it makes my C kernel huge.

but when you use
"int someint;
void somevoid()
{
someint = 20;
}"

it doesn't do that,

right?
Tim

Re:Kernel in C

Post by Tim »

The difference between:

a)
int somevar = 20;
int main(void)
{
return 0;
}

and b)
int main(void)
{
int somevar = 20;
return 0;
}

is that in (a), somevar is put into the .data section of the kernel image and exists on disk, which is how it gets its value at load time. In (b), somevar exists on the stack in the main() function, and isn't created until main() is called.

The reason that your image goes huge when you declare one global variable is that the linker pads each section to 4096 bytes (it does that to .text, the code, too). If you add another global variable, you'll see that the image doesn't grow in size, and it won't grow until you've added enough variables to move onto the next 4096-byte page.

If you do this:

int somevar;
int main(void)
{
somevar = 20;
return 0;
}

...then you won't see any increase in the size of the kernel file on disk, because the linker doesn't need to save the default value of somevar on disk, because it doesn't have one. In this case, somevar goes into the .bss section, and the loader is expected to zero that section at load time.
PlayOS

Re:Kernel in C

Post by PlayOS »

Tim Robinson wrote:The difference between:

a)
int somevar = 20;
int main(void)
{
return 0;
}

and b)
int main(void)
{
int somevar = 20;
return 0;
}

is that in (a), somevar is put into the .data section of the kernel image and exists on disk, which is how it gets its value at load time. In (b), somevar exists on the stack in the main() function, and isn't created until main() is called.
So will be both of these examples be available globally to my kernel, and retain the same value? (ie outside of the main() function)
Tim Robinson wrote:In this case, somevar goes into the .bss section, and the loader is expected to zero that section at load time.
Since my kernel will not be loaded like a normal application, will I have to zero this section in my boot code before jumping to it?

Also, I am not that knowledgable in C, how do I export/import variables and functions from a source file?
Tim

Re:Kernel in C

Post by Tim »

PlayOS wrote:So will be both of these examples be available globally to my kernel, and retain the same value? (ie outside of the main() function)
Clearly the somevar inside main() won't be accessible outside main(). Global variables will though.
Since my kernel will not be loaded like a normal application, will I have to zero this section in my boot code before jumping to it?
Yes (although if you are using GRUB, it will do that for you).
Also, I am not that knowledgable in C, how do I export/import variables and functions from a source file?
To export a function or variable, don't make it static. In other words, all functions and variables without the 'static' keyword are exported.

To import a function, declare the function at the top of your source file or (better) in a header file.
To import a variable, declare the function with the extern keyword.

e.g.:

Code: Select all

---- file1.c
int var1; // Accessible anywhere
static private_var; // Accessible only inside file1.c

// Accessible anywhere
void function1(void)
{
    printf("Hello, world %d\n", var1);
}

// Accessible only inside file1.c
static void private_function(void)
{
}

---- file2.c
void function1(void);
extern int var1;

int main(void)
{
    var1 = 42;
    function1();
    return 0;
}
PlayOS

Re:Kernel in C

Post by PlayOS »

Ah, I see, I had it in my mind that example b, was

int somevar;
int main(void)
{
somevar = 20;
return 0;
}

would this still cause a 4KB data space to be created? or would this assign this value at runtime? Would somevar retain the same value after main() exits?
Tim

Re:Kernel in C

Post by Tim »

PlayOS wrote: Ah, I see, I had it in my mind that example b, was

int somevar;
int main(void)
{
somevar = 20;
return 0;
}

would this still cause a 4KB data space to be created?
It will cause a 4KB bss space to be created in memory, but not on disk.
or would this assign this value at runtime?
No, because the linker know that uninitialised global variables will be zeroed by the loader.
Would somevar retain the same value after main() exits?
Yes, because it's global.
PlayOS

Re:Kernel in C

Post by PlayOS »

Thanks this is all good help. Also, what happens with constants? What would happen in this case:

#define CONSTANT 100

int main( void )
{
int x = CONSTANT;
return 0;
}
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Kernel in C

Post by Pype.Clicker »

a #define applies at preprocessing time. there is no difference in the code generated between

Code: Select all

#define A_DEFINED_CONSTANT 123456
int x = A_DEFINED_CONSTANT
and

Code: Select all

int x = 123456
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Kernel in C

Post by Pype.Clicker »

by the by, your 4Kb problem is due to the fact that by default, ELF files do some padding to speed up file mapping in paged environment. I think there is an option to change the section mapping to - say - 16 bytes, or even just one byte. There is also a way (using complex .ld files :) to make the image on disk have no padding while the memory-mapped image will have some.

unfortunately, i have focused on COFF files, so i don't know the options for ld, but i know this should be feasible...
PlayOS

Re:Kernel in C

Post by PlayOS »

So then all preprocessors have no affect on the disk image.

Ok, maybe I will look into the ELF format if I come to really need this, thanks.
Schol-R-LEA

Re:Kernel in C

Post by Schol-R-LEA »

PlayOS wrote: So then all preprocessors have no affect on the disk image.
Not directly. With the exception of the #pragma directives, which send options to the compiler, all preprocessor directives act on the source code before it is compiled; they are strictly textual replacements.

For a few examples:
A defined name's value is inserted directly into the source code, without regard to it's meaning. Thus,

Code: Select all

#define MYCONST 1234

x = MYCONST;
becomes

Code: Select all

x = 1234;

after preprocessing; this is the code actually seen by the compiler itself. Thus one could write

Code: Select all

int x = 3;
int y;

#define DOUBLEX x*x

y = DOUBLEX;
and get the result

Code: Select all

int x = 3;
int y;


y = x * x;
Macros work the same way, except that they replace the names of their parameters with the text arguments given to them. Thus,

Code: Select all

#define SQUARE(x) x *x
int x, y, z;
char a, b;

y = z = 2;
x = SQUARE(y);
y = SQUARE(y + z);
z = SQUARE(x + y * z);
a = 'a';
b = SQUARE(a);

will result in the following code going to the compiler:

Code: Select all

int x, y, z;
char a, b;

y = z = 2;
x = y * y;
y = y + z * y + z;
z = x + y * z * x + y * z;
a = 'a';
b = a * a;
Determining all of the resulting programming errors is left as an excerise for the reader; let it suffice to say that line 8 (the last line, assuming that line numbering starts at zero) will cause a compiler error, while lines 5 and 6 have logical errors stemming from operator precedence conflicts. This is the classic example of both the power and the danger of too liberal a use of preprocessor macros, which any text on the language which is worth reading should cover in detail. It is important to understand just how powerful they really are: the only limitations on them are that the cannot be recursive (otherwise the preprocessor would get stuck in an infinite loop), and they must use the line extension marker ('/') if they are to span more than one line:

Code: Select all

#define BIGMACRO(x, y, z) if (x > z)                  /
                                              {                                    /
                                                x = pow(y, 4);             /
                                                y = 5;                          /
                                               }                                   /
                                               else                                /
                                               {                                   /
                                                   z = y * x;                  /
                                                   y = (int) time(NULL); /
                                                }
This is an extreme example, but not an impossible one.

Includes add text from other files to the current file.

Code: Select all

#include <stdio.h>

inserts the contents of the file stdio.h into your source code.

Conversely, preprocessor switches control which text should be excluded from the source code.

Code: Select all

srand((int) time(NULL)); /* use current time for random seed */
y = rand();
#ifdef ___SOLARIS___
x += y;
#elif defined ___LINUX___
x += y > 0 ? y : 1;
#elif defined ___WINDOWS___
if (phase_of_moon() != "GIBBOUS" && ulBills_bankroll > MAXULONG)
x += y * (x -y);
#endif
z = factorial(x);
causes only the code for in the clause matching the appropriate defined macro value to appear in the text; so, if the value "#define ___LINUX___" had appeared earlier in the text, the resulting code would be

Code: Select all

srand((int) time(NULL)); /* use current time for random seed */
y = rand();
x += y > 0 ? y : 1;
z = factorial(x);
It is important to remember that all of the changes are solely in the text of the source code, and are completely invisible to the compiler; it is completely oblivious to the proprocessor's very existence (at least in traditional implementations). The preprocessor is in fact a separate program, cpp, in most versions, which the copiler driver program (gcc) calls before running the compiler proper. Depending on the version, it may also do some houscleaning tasks, such as removing comments and redundant lines; some may even compact the source code into a single line of text, for faster compiling.

It should be noted thast most of what has been said about the CPP applies, generally speaking, to assembler macros as well - indeed, in the case of as or gas, the assembly macro processor often is the C preprocessor, though the m4 preprocessor is also often used. While other macro systems (such as MASM's opr NASM's) are more closely tied to the assembler itself, the basic function of all of them is to alter the text of the source code before the actual compiling or assembling takes place.
Post Reply