C: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Reverted edits by Carver (talk) to last revision by Sinetek)
 
(11 intermediate revisions by 7 users not shown)
Line 6: Line 6:


== Libraries ==
== Libraries ==
{{Main|C Library}}

The C library implements the standard C functions (i.e., the things declared in <stdlib.h>, <math.h>, <stdio.h> etc.) and provides them in binary form suitable for linking with user-space applications.
The C library implements the standard C functions (i.e., the things declared in <stdlib.h>, <math.h>, <stdio.h> etc.) and provides them in binary form suitable for linking with user-space applications.


In addition to standard C functions (as defined in the ISO standard), a C library might (and usually does) implement further functionality, which might or might not be defined by some standard. The standard C library says nothing about networking, for example. For Unix-alike systems, the POSIX standard defines what is expected from a C library; other systems might differ fundamentally.
In addition to standard C functions (as defined in the ISO standard), a C library might (and usually does) implement further functionality, which might or might not be defined by some standard. The standard C library says nothing about networking, for example. For Unix-alike systems, the POSIX standard defines what is expected from a C library; other systems might differ fundamentally.


It should be noted that, in order to implement its functionality, the C library must call kernel functions. So, for your own OS, you can of course take a ready-made C library and just recompile it for your OS - but that requires that you tell the library how to call your kernel functions, and your kernel to actually provide those functions. The good news is that [http://sourceware.org/newlib/libc.html#SEC195 relatively few of the library's functions do use some system call].
It should be noted that, in order to implement its functionality, the C library must call kernel functions through system calls. So, for your own OS, you can of course take a ready-made C library and just recompile it for your OS - but that requires that you tell the library how to call your kernel functions, and your kernel to actually provide those functions.


It should also be noted that you can not use a usermode C library directly in your kernel as kernel code needs to be compiled specially. Things such as malloc, free, memcpy need to implemented by you before being used.
Available libraries include the [http://www.gnu.org/software/libc/ GNU C library] (with info about porting the glibc), [http://sources.redhat.com/newlib/ newlib] (with info on the required OS functions detailed in the manual), and [http://www.uclibc.org/ uClibC] (although that is highly optimized to be used with an embedded Linux).

It should also be noted that in most cases you can not use a usermode C library directly in your kernel. Things such as malloc, free, memcpy need to implemented by you before being used. In GCC the "--nobuiltin" flag tells GCC to not use pre-existing builtin functions such as memcpy.


== Things C can't do==
== Things C can't do==
Line 32: Line 32:
To fix possible issues due to optimizations performed by processors on runtime (e.g. reordering instructions and memory accesses, caching memory) make sure you use [[atomic operation]]s or [[memory barriers]] where neccessary. This however isn't an optimization specific to the C/C++ language but rather platform and hardware specific.
To fix possible issues due to optimizations performed by processors on runtime (e.g. reordering instructions and memory accesses, caching memory) make sure you use [[atomic operation]]s or [[memory barriers]] where neccessary. This however isn't an optimization specific to the C/C++ language but rather platform and hardware specific.


However, although optimizations increase the severity and apparent number of small bugs in C code, they will never break correct C, assuming the compiler is stable and multi-threading issues are accounted for. Because of this, it is sometimes best to debug with optimizations enabled at all different levels, to make sure that code is absolutely correct. If optimizations are turned off, the bugs are still there, just less noticeable. A release-quality project should work while enabling any combination of optimizations, although it is much harder to make progress while doing so.
== Learning C ==
If you don't know C and plan to use it for osdeving, it might be profitable to begin with some C language exercises.
A good start (you still need to find tutorials, these are just exercises):
* program that writes a string, asks for your name, writes two line breaks and writes another string and the provided name;
* implement strcopy(), strncopy(), strncmp() and other string manipulation functions. What is a buffer overflow?
* implement memmove() or memcpy(). Can you do it with inline assembly so it be faster?
* use inline assembly to perform CPUID and interpret the results;
* use typedefs to name uint32 for a 32-bit unsigned integer type; what type is signed 16-bit?
* use structs and pointers to implement a singly linked lists.
* divide the program with singly linked lists to main.c, list.c and list.h. What is a header guard?
* learn about inline, static, volatile; ternary operator; casts;
* use C preprocessor to write MAX, MIN macros; to print out the current function name, line number;
* use C preprocessor to write a log function which takes a level for the first argument, and uses printf() on the rest of the arguments if the level is higher or equal to current logging threshold.



== See also ==
== See also ==

* [[Books#C|Learning C]]
* [[C preprocessor]]
* [[C preprocessor]]
* [[C PlusPlus|C++]]
* [[C++]]
* [[C MinusMinus|C--]]
* [[C MinusMinus|C--]]
* [[Bare Bones]]


[[Category:C]]
[[Category:Languages]]
[[Category:Languages]]
[[de:C]]

Latest revision as of 20:25, 25 September 2018

C is a minimalistic programming language which is platform independent and requires no runtime. Because of these qualities it has become by far the most used language in the field of OS development and system programming. It can be compared to a "portable Assembly", as it was initially designed and used for.

Compilers

A complex compiler is not required to compile C because of its minimalistic qualities. As a result many various compilers have been written for C and many will work for OS development. Today the two most common are GCC and Microsoft VC++. GCC being the far most common in open source development.

Libraries

Main article: C Library

The C library implements the standard C functions (i.e., the things declared in <stdlib.h>, <math.h>, <stdio.h> etc.) and provides them in binary form suitable for linking with user-space applications.

In addition to standard C functions (as defined in the ISO standard), a C library might (and usually does) implement further functionality, which might or might not be defined by some standard. The standard C library says nothing about networking, for example. For Unix-alike systems, the POSIX standard defines what is expected from a C library; other systems might differ fundamentally.

It should be noted that, in order to implement its functionality, the C library must call kernel functions through system calls. So, for your own OS, you can of course take a ready-made C library and just recompile it for your OS - but that requires that you tell the library how to call your kernel functions, and your kernel to actually provide those functions.

It should also be noted that you can not use a usermode C library directly in your kernel as kernel code needs to be compiled specially. Things such as malloc, free, memcpy need to implemented by you before being used.

Things C can't do

Because of the platform independent nature of C some parts of OS development can only be done in Assembly. Others can be implemented using inline Assembly, but still require knowledge outside the realm of your high-level language of choice.

The canonical example of code that must be written in pure Assembly is the first-stage bootloader. If you choose to write your own instead of using an existing solution such as GRUB, it must be written in Assembly, as it requires direct manipulation of certain registers; specifically, the segment selectors and the stack pointer, which would not be possible in C itself.

Other functions, such as loading the Global Descriptor Table (on the IA32 architecture), also require special opcodes which are not available within C language (but can be implemented in Inline Assembly). In the (unlikely) case your compiler does not support inline Assembly, you have the option of writing 'support functions' in a separate assembly file. Note that we have a page with examples for basic operations using GCC.

Interrupt Service Routines (ISRs) also require some special handling, because they are called directly by the CPU, not by the C environment.

Things you should know about optimizations

At some point you might want to compile your code with optimizations enabled. This however can lead to unpredictable results if your C code wasn't written properly. You might end up wasting days or weeks trying to find out why your code works flawlessly without optimizations enabled, but as soon as you compile it with optimizations it just doesn't work anymore or even crashes or locks up your OS. Operating systems are usually multi-threaded environments these days, however the optimizer of compilers generally assume that algorithms and memory changes are single-threaded and so only visible to one instance. So if the optimizer determines that your code does something that is redundant and could be optimized away, it might remove code or change it's logic any way it wants. These are legit changes to the code, but in some instance it's not what you want and it will simply break it. To prevent such things from happening, use the volatile keyword where neccessary.

To fix possible issues due to optimizations performed by processors on runtime (e.g. reordering instructions and memory accesses, caching memory) make sure you use atomic operations or memory barriers where neccessary. This however isn't an optimization specific to the C/C++ language but rather platform and hardware specific.

However, although optimizations increase the severity and apparent number of small bugs in C code, they will never break correct C, assuming the compiler is stable and multi-threading issues are accounted for. Because of this, it is sometimes best to debug with optimizations enabled at all different levels, to make sure that code is absolutely correct. If optimizations are turned off, the bugs are still there, just less noticeable. A release-quality project should work while enabling any combination of optimizations, although it is much harder to make progress while doing so.

See also