Page 1 of 2

Implementing Ctrl-C termination (Or something similar)

Posted: Tue Mar 27, 2012 11:13 pm
by thepowersgang
(I've partially mentioned this on IRC, so some may recognise it)

My OS is reaching the point where those little useful features neet to be implemented. One of these is Ctrl-C to terminate a running program in the current terminal.

Does anyone have suggestions on how this should (or could) be implemented? So-far most of the OS is posix-like, but there are parts which I see as too complex or just plain ugly.

The options I see are
  • Keeping a list of processes with a handle open to the terminal in the terminal driver
  • Implementing process group IDs and keeping the owning PGID in the terminal driver
  • Keeping track of the owning thread/process for a terminal, and send the "signals" (may not be posix signals) to that thread and it passes them on
For signaling, I could implement POSIX signals, but those are clunky to integrate nicely. Especially if they end up interrupting a kernel call. Option 2 is to use IPC messages/kernel events that must be checked within `n` milliseconds or the application is terminated (like SIGKILL in POSIX).

(as a note, I have a mechanism similar to signals for error handling, but in that case there is no kernel call to be interrupted, and the process is already running so there is no problem with needing to schedule the userland call)

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 12:35 am
by Brendan
HI,
thepowersgang wrote:My OS is reaching the point where those little useful features neet to be implemented. One of these is Ctrl-C to terminate a running program in the current terminal.
Different pieces of software are different.

Normally, "Ctrl-C" would be like a normal keypress until it gets all the way to whichever application/widget/thing currently has focus, and then that application/widget/thing is responsible for copying the currently selected data (plus a "data type tag") into some sort of "per user storage area". For example, if the user happens to be running 4 different GUIs (on 4 different monitors) then the same "per user storage area" would be shared by all of those GUIs. Of course "Ctrl-X" would be similar, and "Ctrl-V" would do the opposite (retrieve a copy of the "data plus data type tag" from the "per user storage area").

In some cases, (e.g. games) "control" might mean "fire the first weapon" and "C" might be "crouch", so "Ctrl+C" would just be the player trying to fire the first weapon while crouching. The normal behaviour above needs to be handled by the application/widget/thing that currently has focus so that the default/normal behaviour (or, the behaviour dictated by your user interface guidelines) can be overridden by the application/widget/thing in rare cases where it makes sense to do so.

If the application/widget/thing that currently has focus is a special piece of software that emulates a dumb terminal that dates back to the early 1960s (which in turn was based off of teleprinter technology that dates back to the early 1920's), then it may behave differently when it receives "Ctrl-C" and terminate it's child process instead. Of course this is so far from "normal behaviour" that it becomes abnormal behaviour. 8)


Cheers,

Brendan

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 1:51 am
by Solar
What Brendan said.
thepowersgang wrote:...Ctrl-C to terminate a running program in the current terminal.
You are aware that this is not what actually happens, and just used the easier phrasing, did you?

In a traditional terminal environment, Ctrl-C doesn't terminate anything. It raises the signal SIGINT for the application (which isn't POSIX but standard C, by the way), i.e. interrupts the current control flow and executing the corresponding signal handler for SIGINT defined by the application. The handlers installed by the runtime environment can be SIG_IGN or SIG_DFL. If the user installed some other signal handler that does return, execution is expected to continue where it left off.

It's the traditional implementation of SIG_DFL that does the termination of the process (normally through a call to exit()).

(C99, chapter 7.14 "Signal handling <signal.h>", and chapter 7.20.4 "Communication with the environment".)

You are free to do things differently, of course, but this is what C programmers came to expect. (I think some VIM users would be somewhat miffed if Ctrl-C would terminate the editor. :) )

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 3:38 am
by Combuster
And then some people abuse it. The svn client is one such poltergeist - you can't Ctrl+C it at any time even when you have a perfectly valid reason to do so.

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 3:44 am
by thepowersgang
@Solar: yes I am aware as to how it is usually implemented (hence the mentioning of signals and PGIDs in my original post)

That said, I was unaware that signals were part of the C standard... this could make things interesting.

@Brendan: Yeah, I've handled those at another level, this is more for the terminal emulation code (which is bypassed if a graphical program has the screen).

Mostly, the problem is finding out what is running on a terminal and terminating them if a condition is met. From what Solar said, implementing signals and PGIDs (or session IDs, etc) is probably the correct way of doing it. I guess I was just lazy, not wanting to implement signals (which would require hooks all over the place, including architecture-specific ones)

(late addition)
@Combuster: That is an issue that I've seen before, usually Ctrl-Z, then killing the offending PID works :)

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 3:57 am
by Solar
thepowersgang wrote:@Solar: yes I am aware as to how it is usually implemented (hence the mentioning of signals and PGIDs in my original post)

That said, I was unaware that signals were part of the C standard... this could make things interesting.
I know I scratched by head a bit when I tried to understand what crt0.o actually does, how a process is terminated, stuff like that. Working on PDCLib - especially <signal.h> and <stdlib.h> - gave me an epiphany. :)

I don't see where hooks would be needed "all over the place" to implement signals, though. You'd have a limited number of defined signals for each process (conveniently defining SIGINT et al. to constant indexes into the "signal handler" array), with the "standard array" being defined by the runtime (crt0.o). When a signal is raised for a given process, you interrupt the process (if necessary), call the handler, and resume the process. Seems pretty straightforward to me, but then again, I'm just talking theory. :wink:

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 5:04 am
by thepowersgang
Yeah, the theory is simple, but practice requires being able to break execution of a kernel action (select call, or similar wait) if one is running, then schedule the signal handler to execute. That, and the need for per-architecture code to nicely handle returning to userland (although, it could be handled in the syscall common code... might look into that)

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 9:17 am
by Snake
Even when interrupting things like select with a signal it should not be that special. Just wake up it in the same way you wake it up normally, but have the system call that was sleeping check if there are any pending signals after it woke, and if there are any return out of the system call and switch to the user's signal handler.

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 9:54 am
by Solar
Uh... excuse me if I'm dense, but why would I have to interrupt any pending system call before executing a signal handler?

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 10:04 am
by Brendan
Hi,
Solar wrote:Uh... excuse me if I'm dense, but why would I have to interrupt any pending system call before executing a signal handler?
You don't - as I imagine it if a signal interrupts the kernel you'd only set a "signal occurred" flag (and check that flag when you're returning to user-space from the kernel). However, you would need to be able to take a task out of any "waiting for <something>" state (e.g. so it can return EINTR if/when SA_RESTART is not set).


Cheers,

Brendan

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 11:00 am
by Snake
Solar wrote:Uh... excuse me if I'm dense, but why would I have to interrupt any pending system call before executing a signal handler?
Because you want to have a responsive system. If you have some blocking system calls like select it might be days before they finish naturally, but you still want to have the ability to say kill your application without waiting for this long.

Or perhaps you are implying that while running the system call, you can go back to user mode, run the signal handler then return to kernel mode and continue running the system call?

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 11:14 am
by bluemoon
If application do blocking system calls, the process is suspended and removed from scheduler list; there is nothing pending...
When a signal is sent to that process, you wake it up, but instead of resume the previous context, you pass control to the handler (perhaps with temp stack).

The only mess I can image is that if the process get preempted inside that handler, and you end up with two context.

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 12:30 pm
by Solar
Snake wrote:Or perhaps you are implying that while running the system call, you can go back to user mode, run the signal handler then return to kernel mode and continue running the system call?
Exactly. I don't have to wait for the system call to finish before executing the signal handler (which, by nature, is asynchronous, i.e. a separate control flow - which is why you must not dabble with the application's data structures from within a signal handler. See below.)

If the handler makes the application terminate... well, you need some "cleanup service" anyway, e.g. when the application is aborted due to a segfault. Trigger that, and the interrupted system call gets cleaned up along with the rest.
bluemoon wrote:The only mess I can image is that if the process get preempted inside that handler, and you end up with two context.
That's the whole idea, isn't it? I mean, the C99 standard doesn't have the concept of a "thread", but everything that's written in there about signal handlers etc. (like, sig_atomic et al.) gives the distinct impression that they are meant as a separate thread in the application. (I haven't had time to delve into C11, which does add threads, but I'd wager they made that explicit in there.)

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 12:31 pm
by Snake
Or if you allow the signal handler to do more than just setting a flag somewhere. If you permit IO for example you can get yourself into all sorts of trouble.

Re: Implementing Ctrl-C termination (Or something similar)

Posted: Wed Mar 28, 2012 1:50 pm
by rdos
I decided against implementing Ctrl-C and thread/application termination. It's just too messy, and I hardly think it is possible to preempt operations in kernel and terminate the application. That could lead to kernel resources being locked forever. As an example, a thread might owe critical sections, and just terminating it under those conditions could pretty likely lead to a system lock-up. Even worse, the thread may be in the process of being scheduled, and breaking it at that time will be certain to create a panic.

And any thread that is not actively executing something is either waiting for a lock (like a critical section), or is waiting for a signal, so chances are slim that it can be sent a signal in user-space.

What I have implemented is a way to take a thread out of "wait for signal", and immediately put it into debug-mode. This is used by the debugger to stop threads and make them available for debugging. It is implemented by setting a trap-flag for the thread in the scheduler, in combination to sending a signal wake it up. However, the debugger has no way of terminating threads or the application.

OTOH, I have no way of breaking a block for a critical section, and this is partly due to the fact that EnterSection is not expected to return until it has the section, and thus has no error-code which informs the caller if it was successful or not. This makes critical sections more effective, at the expense of no way of breaking the block operation.

In fact, there is a good way to stop an application, and it is called "ctrl-alt-del" (reset). :mrgreen: