4 ms·
Nobody else answered you so I'm going to make up an answer which, even if it's not a specifically correct answer, it's a good answer :) look into a.out and ELF
by fsckboy 3y ago
Nobody else answered you so I'm going to make up an answer which, even if it's not a specifically correct answer, it's a good answer :)
look into a.out and ELF formats for executable files; your executable files, said files hereby called "your code", will be in this format on linux.
There will be an entry point for your code, so if your program gets run, the kernel (or whatever userspace launcher it's given this task to... now that I think of it, from the command line, the shell would fork itself, and one fork would exec your program, so we're talking about exec here) will load your code into memory, and transfer control to--transfer control to means set the program counter to--your code at a particular address (the address of C main(), so to speak). Before this happens, the launcher will have allocated memory for you, set up the stack pointer, opened stdin/out/err, put the command line arguments into place (argc, argv), etc. Then your code can run till it's done, making system calls if it needs to, and potentially being preemptively multitasked, but that's all invisible to your code, black box in terms of how it's implemented.
now for signals...
Signals from the operating system need their own entry point(s) into your code. So, while your code is running, the OS can interrupt it (stuffing everything of yours onto your stack so it can be popped off later when control flow is returned to the same point later). In this meantime, the OS will now transfer control (set the program counter just like it did when it first launched you) and you're off and running with code to handle the signal, the interrupt.
If you have not prepared your code for this event, the default (set up by the launcher for programs) program flow at this point will be sent to a generic handler that says "unhandled signal", and your program will be killed. They're actually lying, the signal is being handled, but it's being handled by a handler that cleans up and exits.
But, you can write code to handle the signal yourself, and when your program launches you (generally in main() or nearby) register the address of the handler you want to use so that when the interrupt comes the OS will know where to send control. At that point it's as if you just called a function/subroutine at wherever your code running at that moment, and when that function returns, things continue as they were before, modulo whatever you did in your handler.
There's a bunch of specifics I don't know, is there one entry point for all signals, and a signal argument token decides where to transfer control, or are there 10 separate handlers so you don't waste time? etc.
if you're doing no-wait I/O with a single thread, one way is to make a blocking call, call into something that doesn't return till there's new data, and that new data may then arrive, handled by your interrupt handler; in that case returning from your input-getting handler could return from the blocking call and your code is off and running as if it just returned in the first place. There are a myriad variations on this, but that's the general idea.
happy to clarify anything, others can probably answer better, etc.
- omginternets 3y agoFirstly, thank you. Your comment is relating a couple of distinct ideas together in a way that is very helpful. >Then your code can run till it's done, making system calls if it needs to, and potentially being preemptively multitasked, but that's all invisible to your code, black box in terms of how it's implemented. I'm realizing that I don't actually understand how pre-emptive multi-tasking is implemented at the VM level, and I think this is perhaps what I actually want. Can you elaborate and/or suggest any articles/video-lectures on the subject? Maybe what I need is an operating-systems course...
- h2odragon 3y agoI think glibc or equiv provides the default signal handlers as part of the stuff that gets included with even "hello world" to make it 10kb (or 10mb, whatever your tools emit).
- monocasa 3y agoThe default actions are handled by the kernel so that they still exist even before glibc has a chance to come up (for instance so core dumps happen even in early dynamic linking).