Hey, I started to develop my own os, my finale result I want is a shell screen that the user can write commands like "ls", "echo" and so on..
I just success to develop the kernel and I moved to the user mode.
I have some questions about it, hope you can help me.
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?
I think that I will have a shell code that call a systemcall for printing to the screen and when you in the syscall you will be in kernel mode, and the user can write till he will press enter and then the user will be back to user mode and the command he was write will call to the next syscall.
just to be clear:
usermode
syscall - read from keyboard
kernelmode
ls (command example)
usermode
and then calling the ls handler.
this is how I should do it?
if no please can you tell me how I should do the user mode to work right?
thanks for your help!
Trying to move to usermode
-
- Member
- Posts: 71
- Joined: Fri Jun 28, 2013 1:48 am
- Contact:
Re: Trying to move to usermode
First you need to handle interrupts properly under kernel mode.
Then make interrupts also work under user mode. Same applies to system call.
So when user types on the keyboard, the interrupt will cause CPU switch into kernel mode, so your kernel code can read the scan code.
When your user-mode application want to print something on the screen, do a system call and switch into kernel mode, then your syscall handler will do the actual job on behalf of user-mode programs.
Then make interrupts also work under user mode. Same applies to system call.
So when user types on the keyboard, the interrupt will cause CPU switch into kernel mode, so your kernel code can read the scan code.
When your user-mode application want to print something on the screen, do a system call and switch into kernel mode, then your syscall handler will do the actual job on behalf of user-mode programs.
Reinventing the Wheel, code: https://github.com/songziming/wheel
Re: Trying to move to usermode
Hi,
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):
Cheers,
bzt
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.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?
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 |
bzt
-
- Posts: 5
- Joined: Mon Feb 25, 2019 12:47 pm
Re: Trying to move to usermode
songziming wrote:First you need to handle interrupts properly under kernel mode.
Then make interrupts also work under user mode. Same applies to system call.
So when user types on the keyboard, the interrupt will cause CPU switch into kernel mode, so your kernel code can read the scan code.
When your user-mode application want to print something on the screen, do a system call and switch into kernel mode, then your syscall handler will do the actual job on behalf of user-mode programs.
Hey, can you tell me how I need to handle my interrupts to make them work in user mode?
Re: Trying to move to usermode
Well that's a vague question.
First, the keyboard actually needs to generate an interrupt. If you truly are still working with a PS2 controller, I suggest you read up on that in the wiki. Check that both keyboard processing is enabled and interrupts are not masked there.
Then the interrupt needs to be sent to the CPU. The PIC or APIC is responsible for this. You need to ensure whatever PIC you're using is set up correctly and is sending the keyboard IRQ through to the CPU as an INT.
Then the interrupt needs to be recognized. You need to ensure the interrupt flag is set. You propably go into user space with the IRET instruction. That instruction will pop flags (the third word from the stack). If the IF is not set there, your userspace program will run without interrupts enabled, and thus it won't recognize keypresses.
Then you actually need to handle the interrupt. Whatever INT is generated when the keyboard IRQ fires needs to have an interrupt gate set up in the IDT.
Check back once you know which part of this process you are hanging on.
First, the keyboard actually needs to generate an interrupt. If you truly are still working with a PS2 controller, I suggest you read up on that in the wiki. Check that both keyboard processing is enabled and interrupts are not masked there.
Then the interrupt needs to be sent to the CPU. The PIC or APIC is responsible for this. You need to ensure whatever PIC you're using is set up correctly and is sending the keyboard IRQ through to the CPU as an INT.
Then the interrupt needs to be recognized. You need to ensure the interrupt flag is set. You propably go into user space with the IRET instruction. That instruction will pop flags (the third word from the stack). If the IF is not set there, your userspace program will run without interrupts enabled, and thus it won't recognize keypresses.
Then you actually need to handle the interrupt. Whatever INT is generated when the keyboard IRQ fires needs to have an interrupt gate set up in the IDT.
Check back once you know which part of this process you are hanging on.
Carpe diem!