Keyboard input

Programming, for all ages and all languages.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Keyboard input

Post 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).
User avatar
iansjack
Member
Member
Posts: 4686
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Keyboard input

Post 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.
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: Keyboard input

Post 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".
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: Keyboard input

Post 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.
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard input

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: Keyboard input

Post 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.
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
User avatar
xenos
Member
Member
Posts: 1118
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Keyboard input

Post 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.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Keyboard input

Post 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.
rdos
Member
Member
Posts: 3277
Joined: Wed Oct 01, 2008 1:55 pm

Re: Keyboard input

Post 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.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Keyboard input

Post 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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Keyboard input

Post 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
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: Keyboard input

Post 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! ;)
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Keyboard input

Post 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
rdos
Member
Member
Posts: 3277
Joined: Wed Oct 01, 2008 1:55 pm

Re: Keyboard input

Post 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.
User avatar
Jezze
Member
Member
Posts: 395
Joined: Thu Jul 26, 2007 1:53 am
Libera.chat IRC: jfu
Contact:

Re: Keyboard input

Post 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.
Fudge - Simplicity, clarity and speed.
http://github.com/Jezze/fudge/
Post Reply