5

When we compile a C program, it just generates some machine-understandable code. This code can directly run on the hardware, telling from this question.

So my questions are:

  1. If a C program can directly run on the hardware, how can the kernel handle the resource allocation for this program?

  2. If the executable generated from the compiler is in pure machine-understandable form, then how do the privileged and non-privileged modes work?

  3. How does the kernel manage the permission of hardware resources if a program can directly run on the hardware not through the kernel?

Community
  • 1
  • 1
shiv garg
  • 671
  • 1
  • 7
  • 25
  • No. Assuming a non-trivial OS, it cannot even get loaded into memory without kernel calls, never mind obtain execution. – Martin James Oct 21 '15 at 17:10
  • 1
    "When we compile the C program it just generates the machine understandable code. This code can directly run on the hardware." - This machine understandable code does system calls to the kernel to allocate resources. If you want to go bare metal, your application will be loaded on boot and then, yeah, you can do whatever you want. Except for that, you're in line with whatever the kernel allow. – user430051 Oct 21 '15 at 17:48

4 Answers4

5

Whilst the program is in machine code, to do anything not within its own memory region, it will need to call the kernel via syscalls.

The CPU actually has a notion of the privilege of code. Unprivileged code can't directly access physical memory etc; it has to go through the OS and ask it to give access.

Hence, every program does directly run on the CPU, but that doesn't mean it can do anything with the hardware – there's hardware measurements against that. The privilege that you'd need to do certain things is one of these.

Marcus Müller
  • 27,924
  • 4
  • 40
  • 79
  • i think sys calls are in non binary format that kernel understands. so either the executable contain the non machine understandable components (non binary) or system calls must be in binary format. which of the argument is right? – shiv garg Oct 21 '15 at 17:28
  • no. the kernel doesn't "understand" any of this. It's being *called* by the program in cquestion. None of your arguments are right, and you will have to study what computers work like a little more. – Marcus Müller Oct 21 '15 at 17:30
  • ok you want to tell that when program compiles all the information gets embedded in it, about the resources, how much can it use – shiv garg Oct 21 '15 at 17:32
  • exactly, the job of the compiler and linker is to transform your source code into machine code, which might contain calls to symbols e.g. from userland libraries and also might contain calls to the kernel. The linker takes the names of the functions called, and transforms them basically to addresses - and there's no trace left of the original names of the kernel functions left in your executable. – Marcus Müller Oct 21 '15 at 17:39
5

If a C program can directly run on the hardware how can kernel handle the resource allocation to this program.

The kernel is responsible for managing the resources of the computer as a whole, including resources like the hardware. This means that, for user-level applications to be able to access things like hardware devices, write to a terminal, or read a file, they have to ask the kernel for permission. This is done by using the system calls exposed by the OS, as mentioned by @Marcus.

However, I wouldn't say that the program runs directly on hardware in the sense that it does not interact with the hardware directly, as a kernel module/driver would. A client program will set up the arguments for a system call and then interrupt the kernel and wait until the kernel services the interrupt request the program made.

This is why OSes today are said to run in protected mode, as opposed to the old days when they ran in real mode and a program could, for example, mess around with hardware resources directly --and potentially screw things up.

This distinction becomes very clear if you try writing a trivial "hello world" program in x86 assembly. I wrote and documented this one several years ago, reproduced below:

;
; This program runs in 32-bit protected mode.
;  build: nasm -f elf -F stabs name.asm
;  link:  ld -o name name.o
;
; In 64-bit long mode you can use 64-bit registers (e.g. rax instead of eax, rbx instead of ebx, etc.)
; Also change "-f elf " for "-f elf64" in build command.
;
section .data                           ; section for initialized data
str:     db 'Hello world!', 0Ah         ; message string with new-line char at the end (10 decimal)
str_len: equ $ - str                    ; calcs length of string (bytes) by subtracting the str's start address
                                            ; from this address ($ symbol)

section .text                           ; this is the code section
global _start                           ; _start is the entry point and needs global scope to be 'seen' by the
                                            ; linker --equivalent to main() in C/C++
_start:                                 ; definition of _start procedure begins here
    mov eax, 4                   ; specify the sys_write function code (from OS vector table)
    mov ebx, 1                   ; specify file descriptor stdout --in gnu/linux, everything's treated as a file,
                                             ; even hardware devices
    mov ecx, str                 ; move start _address_ of string message to ecx register
    mov edx, str_len             ; move length of message (in bytes)
    int 80h                      ; interrupt kernel to perform the system call we just set up -
                                             ; in gnu/linux services are requested through the kernel
    mov eax, 1                   ; specify sys_exit function code (from OS vector table)
    mov ebx, 0                   ; specify return code for OS (zero tells OS everything went fine)
    int 80h                      ; interrupt kernel to perform system call (to exit)

Notice how the program sets up the write system call, sys_write, and then specifies the file descriptor of where to write, being stdout, the string to write, and so on.

In other words, the program itself does not perform the write operation; it sets things up and asks the kernel to do it on its behalf by using a special interrupt, int 80h.

A possible analogy here might be when you go to a restaurant. The server will take your order, but the chef is the one that will do the cooking. In this analogy, you are the user-level application, the server taking your food order is the system call, and the chef in the kitchen is the OS kernel.

If the executable generated from the gcc is in pure machine understandable form then how do the privileged and non-privileged mode work?

Keying off from the previous section, user level programs always run in user mode. When the program needs access to something (e.g. terminal, read a file, etc.), it sets things up, as with the sys_write example above, and asks the kernel to do it on its behalf with an interrupt. The interrupt causes the program to go into kernel mode and remains there until the kernel has completed servicing the client's request --which may include denying it altogether (e.g. trying to read a file the user has no privilege to read).

Internally, it's the system call that's responsible for issuing the int 80h instruction. User-level applications just see the system call, which is the common interface between the client and the OS.

How does the kernel manage the permission of hardware resources when a program can directly run on hardware not through the kernel?

If you followed the previous explanations, you can now see that the kernel acts as a gatekeeper and that programs "knock" on this gate by using the int 80h instruction.

code_dredd
  • 5,368
  • 1
  • 21
  • 48
3

how can kernel handle the resource allocation to this program

The kernel provides functions and mechanisms to allocate memory, do I/O (write to the screen, interact with the network/sound card), etc., called system calls to user programs. These system calls are the interface between kernel and user programs, alas between the hardware and user programs.

how do the privileged and non-privileged mode work?

User programs are in Unprivileged Mode (userspace) while the kernel runs in Privileged Mode (kernelspace). The user can't be trusted, so if he messes up (accesses higher-privileged memory or dereferences a null pointer, for example) he's prevented from it (by a segmentation fault and the following termination of the program, for example).

The kernel on the other hand runs in Privileged Mode. It can do whatever it wants: write to userspace programs, steal data (like passwords) from user programs, write to the processor's firmware - everything. Furthermore, there are different kinds of kernels: monolithic kernels and microkernels are the heaviliest (does that word exist at all?) used ones.

Linux (initiated by Linus Torvalds) is an example for a monolithic kernel. Here, the kernel is one big system, where every piece of kernel code has ultimate access to the system.

Minix (initiated by Andrew S. Tanenbaum) is an example for a microkernel. The part, which can access everything is rather small. It contains only the functionality that has to be privileged (managing the MMU, accessing hardware), etc. Other functionality, like filesystems, run in the Unprivileged Mode, where they are protected from possible bugs by the usual protection mechanisms employed in userspace (Unprivileged Mode), like Segmentation Faults.

An interesting read concerning the benefits/drawbacks of monolithic kernels and microkernels is the debate between Linus Torvalds (at that time some guy who created an OS) and Andrew S. Tanenbaum (at that time an established professor for CS; has written some amazing books, BTW).

a program can directly run on hardware not through the kernel

It indeed runs directly on the hardware, executed by the CPU. It cannot access certain resources directly, though, like memory, and, in order to access these resources, is required to interact with the kernel. That's one of the major improvements (next to maybe virtual processors, that is, processes) over earlier OSes like DOS: userspace programs cannot run directly on the hardware. If they could, they possibly could mess up the whole machine with irreparable causes (intentionally - like viruses, or unintentionally). Instead, as mentioned in the beginning of this answer, system calls are used.

In DOS you had the option to use routines provided by the OS (commonly a trap at IV (Interrupt Vector, the offset (and physical memory address) into the Real Mode IDT (Interrupt Descriptor Table)) 0x21 (invoked via int 0x21/int 21h), while ax contained a function number identifying the call to the system1). Roughly the same mechanisms as nowadays where available but not strictly enforced. One could overwrite the whole OS, replace it with one's own program and destroy the machine (load random values into CMOS registers, for example). One could also just use the BIOS-provided routines, bypassing the OS.


1 I use "call to the system" instead of "system call" intentionally here. Here, system calls only denote the requests from userspace to kernelspace to do something for it. As DOS (i.e., Real Mode) didn't provide a real distinction between userspace and kernelspace, it doesn't really have system calls.

cadaniluk
  • 14,449
  • 2
  • 34
  • 61
  • good answer! Note that there's actually [further rings of protection](https://events.ccc.de/congress/2014/Fahrplan/events/6129.html) against firmware updates that not even a privileged process can't do. But that's totally irrelevant for anyone but hardware designers and really frightening attackers. – Marcus Müller Oct 21 '15 at 17:35
  • I don't really agree with the "it doesn't run on the actual hardware" bottom line, though: *Of course* the program is actually run by the actual CPU (it's not being instrumentalized e.g. like in an emulator), but the execution is severely restricted. – Marcus Müller Oct 21 '15 at 17:36
  • @MarcusMüller: I think you've missed the point. The fact that the physical CPU executes the code is not the issue. He's clearly talking about the different modes under which a program can execute. The fact remains that a program cannot directly mess around with hardware resources and stuff in the context of the distinction between usermode & kernelmode. – code_dredd Oct 21 '15 at 17:49
  • @ray: I'd agree with you, but I don't think OP is at that level of understanding. – Marcus Müller Oct 21 '15 at 17:50
  • @MarcusMüller: You may be right, but that still does not change the fact that the distinction is accurate. The OP will get to that level sooner or later, if he/she really isn't there yet. – code_dredd Oct 21 '15 at 17:52
  • 1
    @ray: Ok, can we agree on this: The bottom line is that whilst the program does directly run on the CPU, it's limited privilege inhibits direct access to hardware and physical memory in general. – Marcus Müller Oct 21 '15 at 17:54
  • 1
    @MarcusMüller: We can indeed. The distinction should be fairly easy for the OP to get. – code_dredd Oct 21 '15 at 17:56
  • @Downvoter Hey, what's wrong (I mean the answer)? If its because of the reason MarcusMüller gave in the first comment to this answer, I'll fix that now. – cadaniluk Oct 21 '15 at 18:18
  • @MarcusMüller Edited it, is it disambiguated now? – cadaniluk Oct 21 '15 at 18:21
  • @MarcusMüller Regarding your first comment with the "further rings of protection", I don't see the OP mention x86 specifically anywhere. Also, I wasn't talking about "rings of protection" but about privilege modes, which are one abstraction level above "rings", IMO. And thank you of course for your "good answer!". :-) – cadaniluk Oct 21 '15 at 18:24
2

So my first question is if a C program can directly run on the hardware how can kernel handle the resource allocation to this program.

CPUs carry a notion of privileges when executing code. For example, on x86 there is a Real Mode where code is allowed to access any resource, and a Protected Mode where code executes in distinct security rings. Most Operating Systems will switch to Protected Mode, where numerically lower rings imply higher privileges.

The kernel typically executes in Ring 0, which gives direct access to the hardware, while user programs run in Ring 3 which restricts access. When the user program needs to access a privileged resource, the CPU calls into the Operating System, which is privileged, either implicitly or directly through a system call instruction (e.g. syscall in x86-64 assembly).

If the executable generated from the gcc is in pure machine understandable form then how do the privileged and non-privileged mode work?

Again, things like memory access are checked by the CPU. So for example if a program tries to access a virtual address that it doesn't have permission for, the Operating System catches the invalid page access and generally signals the process (i.e. SIGSEGV).

How does the kernel manage the permission of hardware resources when a program can directly run on hardware not through the kernel?

The CPU has to interact directly with the Operating System through specific control registers and tables. For example, the address of the virtual address page table is stored in the CR3 register for x86.

Jason
  • 3,677
  • 12
  • 24