Page 1 of 2

Keyboard input

Posted: Fri Nov 09, 2012 1:55 pm
by Antti
I have been wondering "which process gets the keyboard input."

Code: Select all

#include <stdio.h>
#include <unistd.h>

#define BUFFERSIZE 0x10

int main()
{
        pid_t pid;
        char buffer[BUFFERSIZE];

        pid = fork();
        if (pid == -1) return 1;

        if (pid == 0) {
                printf("Child Process\n");
                fgets(buffer, BUFFERSIZE, stdin);
                printf("Child: %s\n", buffer);

        } else {
                printf("Parent Process\n");
                fgets(buffer, BUFFERSIZE, stdin);
                printf("Parent: %s\n", buffer);
        }

        return 0;
}
How this should behave? I have no technical problems here. It is about policy. When I run this on "real system" (Linux + bash), it behaves so that Parent gets the input and prints normally. Child never gets input but prints "Child: " much later (after a couple of enter button hits).

Re: Keyboard input

Posted: Fri Nov 09, 2012 2:13 pm
by iansjack
I don't believe there is an answer to your question. I think in this scenario it is undefined as to which process will get keyboard input. Depending upon the OS implementation it might be the parent, it might be the child, or it might be random.

The real answer is that such a program doesn't make sense. (Sure you can write such a program, but you shouldn't.) Why would you want two foreground processes active in the same console both reading the keyboard? There is no logical reason to do that.

Re: Keyboard input

Posted: Fri Nov 09, 2012 2:22 pm
by bluemoon
Fork() causes creation of a new process. The new process (child process) is an exact copy of the call-
ing process (parent process) except for the following:

o The child process has a unique process ID.

o The child process has a different parent process ID (i.e., the process ID of the parent
process).

o The child process has its own copy of the parent's descriptors. These descriptors reference
the same underlying objects, so that, for instance, file pointers in file objects are shared
between the child and the parent, so that an lseek(2) on a descriptor in the child process
can affect a subsequent read or write by the parent. This descriptor copying is also used by
the shell to establish standard input and output for newly created processes as well as to
set up pipes.

o The child processes resource utilizations are set to 0; see setrlimit(2).
So, if you are compatible with POSIX, in your case the child process race with the parent for getting input from the "same descriptor", just as 2 threads trying to read the same file - however, which thread get the data is "first come first serve".

Re: Keyboard input

Posted: Fri Nov 09, 2012 2:38 pm
by Jezze
I don't believe in explicitly reserving keyboard input for any task but instead either:

1, First come first served. All tasks will be notified that data is available and the first reading it will get the data. It is easy to implement but gives unpredictable results.
2, Multiplexing. All tasks will be notified that data is available and then they each will be able to read the data out. It is a bit more harder to implement but gives predictable results.

I strongly believe in 2 because I think a kernel should act as a multiplexer of resources.

Re: Keyboard input

Posted: Fri Nov 09, 2012 3:25 pm
by Brendan
Hi,

If we're voting; then I prefer "parent keeps keyboard input" on the basis that the child is likely to replace its stdin/stderr/stdout and then "exec()".

Of course I also think the entire "fork() then exec()" thing is stupid; and it's better to do something like "spawn_process(exe_file, args, env_vars, std_pipes)" and skip a lot of pointless work (cloning address space, descriptors, etc then destroying them) while also avoiding a lot of confusion and mess. ;)


Cheers,

Brendan

Re: Keyboard input

Posted: Fri Nov 09, 2012 5:14 pm
by Jezze
I have not made serious effort trying to understand all the complexity of fork(), exec() and (let's just call it) spawn() but as of now I am on the same page as you Brendan. I noticed that spawn() is a lot easier to implement and also more intuitive than fork(). fork() makes things complicated, both from the kernel perspective and from a programming perspective, and what you gain by it I am not sure because most of the time you use fork() it is almost always followed by an exec() anyway. Also the resulting process made from spawn() will always be in the same fresh state every time compared to fork() where the process might look different depending on what the parent process state looked like.

Re: Keyboard input

Posted: Sat Nov 10, 2012 12:02 am
by xenos
I totally agree with Brendan and Jezze about spawn() (in my OS it's called CreateProcess() - greetings from the Windows API...) vs. fork() and exec(). I think that's much simpler to implement. But I don't want to feed an uprising flamewar...

Regarding the original question, let me contribute another opinion: I think it should be up to the parent process whether it donates stdin / stdout (or maybe some redirected pipes) to the child process:
  • Imagine calling some child process that requires some user input. In this case one would certainly want to pass stdin to the child, so the user can enter this input in the same console. When this program quits, it returns stdin to its parent.
  • Imagine calling some child process for a background task, while still communicating with the user in the forground task. In this case one would rather want the parent to keep stdin and not pass it to the child process.

Re: Keyboard input

Posted: Sat Nov 10, 2012 4:01 am
by Antti
Thank you for replies. I will do this huge change:

Code: Select all

#include <stdio.h>
/* #include <unistd.h> */
Background information: I have currently implemented many virtual consoles and they can be activated by pressing F1, F2, F3, etc. Every console runs a command-line interpreter called Admin Console. It is a built-in process that cannot be terminated. It runs in user space but it has some special privileges when compared to normal processes. It can do some system calls that are otherwise forbidden.

Every process has its own input buffer. There is always just one process that receives keyboard input. Keyboard driver puts characters to that input buffer. When I change my virtual console, the active process is changed also to the "active process of that virtual console." When I want to start a program, the command is something like start program. The new process is then active (for keyboard input) to that virtual console. This works well when there is only one process per virtual console (in addition to Admin Console). When the program exits, the active process is Admin Console again. Simple.

I am now finding the best solution to handle this "active process thing" when the "real process" creates another process. I know my system is not even close to be POSIX compliant. However, I like this very simple behaviour. I am already thinking whether I should allow the process creation at all for normal programs... It could be that only my Admin Console can start programs. Then there is no traditional "parent-child tree". It would be simply something like that:

Code: Select all

- Admin Console 1     (Active Program: 3)
	- Program 1
	- Program 2
	- Program 3
	- Program 4

- Admin Console 2     (Active Program: 6)
	- Program 5
	- Program 6
	- Program 7
	- Program 8
Then there should be some user-friendly way to change the active process inside the virtual console. Maybe something like:

Code: Select all

Press Esc to activate Admin Console. Then type "activate < program>"
I am sorry that this post is not very well structured. This is stream of thoughts.

Re: Keyboard input

Posted: Sat Nov 10, 2012 12:11 pm
by rdos
I typically use the "CreateProcess" interface, and part of CreateProcess is to create a new console. The keyboard input (as well as visible display output) will go to the process that has input focus. Typically, executing a new program will also put it in input focus, which means the child (executed program) will get input focus and thus keyboard input.

I agree with Brendan and Jezze that fork / exec is a terrible interface. I did some work on it, but the handle inheritance issue is really complex, and most of the time, useless as well.

Re: Keyboard input

Posted: Sat Nov 10, 2012 12:38 pm
by Antti
I try to avoid the feature creep. Of course all the redirections of inputs/outputs, pipes, etc. belong to the concept of real OS (that is defined by "Unix"). However, they are not must have for my simple OS. However, tomorrow I may want to have them.
rdos wrote:I typically use the "CreateProcess" interface, and part of CreateProcess is to create a new console. The keyboard input (as well as visible display output) will go to the process that has input focus.
That is actually quite simple. It seems that running many programs in the same console is a mess. However, a new console is overkill for programs that do not output anything.

Re: Keyboard input

Posted: Sat Nov 10, 2012 2:14 pm
by Owen
For POSIX, the behaviour is defined by the TTY interface (so go and look at the POSIX specification of TTY behaviour)

For two foreground processes, as exists after a fork(), POSIX states that I/O is served in FIFO order IIRC

Re: Keyboard input

Posted: Sat Nov 10, 2012 3:55 pm
by Jezze
Redirection does not have to be a problem when not using fork() if the new process inherits them from it's parent. Meaning the parent changes it's stdin/stdout before calling spawn() and then set them back afterwards. Perhaps not a good idea but at least it does not exclude being able to change them.

And for the record: Cool that we agree on stuff for once! ;)

Re: Keyboard input

Posted: Sun Nov 11, 2012 12:21 am
by Antti
My fork() implementation was not actually according to the standard. It created a new process with another input buffer. Luckily I already "removed" unistd.h. Now the process creation interface is called New(). Parameters are not defined yet.
"I don't understand the reference implementation. I do my own that is simple and elegant." -Antti

Re: Keyboard input

Posted: Sun Nov 11, 2012 5:03 am
by rdos
Jezze wrote:Redirection does not have to be a problem when not using fork() if the new process inherits them from it's parent. Meaning the parent changes it's stdin/stdout before calling spawn() and then set them back afterwards. Perhaps not a good idea but at least it does not exclude being able to change them.
I have a rather unstandard way of handling redirection and inheritance in the c library. I have no support for handle inheritance at all, but multiple processes can use the same resource by opening a new handle to the resource. Thus, I pass a string of redirected handles from the creator to the newly created process, and then part of the initialization in the c library will reopen the handles that are redirected. This can be extended to inheritance at user-level as well if required. This way of handling redirection actually works between executable formats, provided the c library implementation is handing the redirection parameter.

I also do not support opening printers, sockets or communication ports as "file handles", and thus standard input / output cannot be redirected to such sources. It can be implemented at user-level, but unless there is some compelling reason to do that, I won't bother. In my system, every resource has it's own handle-type, and a handle can only be used with the functions that are assigned to it and nothing more. This is a clean, object-oriented, approach. Handle directly translates to object.

Re: Keyboard input

Posted: Sun Nov 11, 2012 7:39 am
by Jezze
Well, what is compelling about it should be pretty obvious and I'm unsure if I really need to explain why? A program writing data to stdout does not need to know anything about the target device. It could be a printer, a framebuffer or a serial console but it does not matter from the program's perspective, it will just work.

I understand that thinking of everything as streams might not feel clean from your perspective but consider the problem you will have when you add a new device type to your os. You will have to modify all programs who wants to utilize that device. Each program will eventually grow incredibly large and difficult to maintain. It would be a nightmare.