Hi,
lidorelias3 wrote:1) When im in user mode, i can't get keyboard interrupt or any other interrupt is it suppose to be?
2) If yes, how do I build the shell, if the user can't write with keyboard or the os cant print to screen?
As songziming wrote, you should get your interrupt code right first. Interrupt Service Routines (or interrupt handlers) must be implemented in the kernel, can't be otherwise.
The interrupt handler and the syscall should be very similar, the only difference is, the interrupt is raised by the interrupt controller, and the syscall is issued by the user code. The reason why early Linux kernels used int 0x80 for syscalls is because of this obvious similarity. These days you can still find that for compatibility reasons, but mostly sysenter or syscall instructions used to pass calls to the kernel.
In both cases, you must receive inputs and generate outputs. For the keyboard, the input is read from the keyboard controller, which is a scancode, and has to be converted to an ASCII character. With print, the input is a memory address of a string. As for the output, the character code should be passed to userland, and in case of print the return value can be omitted (but you could return the number of printed characters for example).
How to connect the user land to receive the return value is the big question here. Some simple kernels do a getc() system call, which waits until a key arrives, and therefore the kernel knows which task executed the getc() and to which to return the character. More sophisticated kernels would keep track of the focused application, and would return the character to the focused one.
But on UNIXes, the proper way to do this is to implement pipes. The user code uses getc() to read from the pipe, which is empty, therefore the task blocks. More tasks can do this at the same time, because they all have their own pipes, and nobody cares if there are more blocked tasks waiting for pipe input. Now when the interrupt handler gets called in the kernel, it would select the focused application's pipe and put the character there, and would unblock that particular task. That task would then read the character from it's pipe. How the focused pipe is selected usually includes another abstraction level, called ttys or pseudo-ttys like in Linux for example.
In a micro-kernel, there are even more layers to be dealt with: the kernel interrupt handler should identify the keyboard task, and pass control to it. Then the user mode keyboard task would read the scancode, convert it to a character and then send it to the tty service. The tty service would then find the pipe's file descriptor assossiated with the focused tty, and would pass the character to the file system service. The file system service would place the character in the pipe, and would call a system service to unblock the user task wanting to read from its stdin. After that point there are no differences between micro- and monolitic kernels, the unblocked user task would read the character from it's stdin pipe.
In UNIX all characters written to the stdout pipe would be read by the tty (either service or kernel subsystem), and would be displayed. Another twist, if a user task is not blocked waiting for input when a key is pressed on the keyboard, then the character would be written to the application's stdout pipe instead of the stdin pipe. From there it would go the tty which would display it. This is called default echo, and you can turn this behaviour off by setting a flag in the tty descriptor struct associated. You can test this by creating a simple "int main() { while(1); return 0; }" application and run it on Linux. Because the application does not read the character from the stdin pipe, all keys you type will be sent to its stdout instead of stdin, and therefore will be read by the tty handler and displayed automatically.
This means if your application reads from the stdin, then it's the application's responsibility to print out the character received to the stdout pipe, otherwise it won't get displayed.
See
https://wiki.osdev.org/Unix_Pipes. Maybe a figure would be propriate to help understanding (monolithic kernel design):
Code: Select all
kernel | user land
------+-------+-----------+-------------
ISR | tty | fs | shell
------+-------+-----------+-------------
IRQ1 -> tty1 -> stdin -> | getc()
| |
| V
tty1 <- stdout <- | putc()
| |
V |
framebuffer renderer |
Cheers,
bzt