Page 1 of 1

Kernel in C

Posted: Thu Sep 05, 2002 9:52 am
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?

Re:Kernel in C

Posted: Thu Sep 05, 2002 12:23 pm
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...)

Re:Kernel in C

Posted: Thu Sep 05, 2002 2:02 pm
by CoolnessItself
Is there a way around it?

Re:Kernel in C

Posted: Thu Sep 05, 2002 2:58 pm
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?

Re:Kernel in C

Posted: Thu Sep 05, 2002 3:58 pm
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.

Re:Kernel in C

Posted: Thu Sep 05, 2002 6:59 pm
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?

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:14 am
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;
}

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:25 am
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?

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:28 am
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.

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:36 am
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;
}

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:55 am
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

Re:Kernel in C

Posted: Fri Sep 06, 2002 6:58 am
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...

Re:Kernel in C

Posted: Fri Sep 06, 2002 7:03 am
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.

Re:Kernel in C

Posted: Sat Sep 07, 2002 11:34 am
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.