4 ms·
There's a lot of cool kings you can do with sigaction. Trapping sigsegv is a great way to do memory management. void sa(int _,siginfo_t*s,void*__){
by geocar 9y ago
There's a lot of cool kings you can do with sigaction.
Trapping sigsegv is a great way to do memory management.
void sa(int _,siginfo_t*s,void*__){
mmap((void*)((~PAGESZ-1)&(uintptr_t)s->si_addr),PAGESZ,
PROT_READ|PROT_WRITE,MAP_FIXED|MAP_ANONYMOUS,-1,0);
}
Want automatic logging of a big on-disk data structure? mmap some pages read-only map_private, trapping writes and logging the address (then remapping the page read+write). Then at "save time", write out page contents to a replay log, fsync+fdatasync+fcntl-F_FULLFSYNC, then rewrite the file using pwrite/write.
You can also implement big sparse hashmaps this way: Every time a server gets a request, it allocates a cell and tells the client about it. Those cells are read+write. If a cell gets checked in, the server simply decrements the counter, but if there's a page fault (read-only) the server can then take the slower path of finding the server that actually responds to it.
And so on.
- pm215 9y agoYou have to be a little careful with trapping SIGSEGV to implement funky behaviour though -- it's easy to write a handler that works for the expected case, but harder to write one that doesn't result in incredibly confusing behaviour if the rest of the program runs into a bug that would normally be reported as SIGSEGV->program core dumps... (Also, mmap() isn't async-signal-safe in POSIX, so this is veering into OS-specifics and reliance on implementation details.)
- geocar 9y ago> it's easy to write a handler that works for the expected case, but harder to write one that doesn't result in incredibly confusing behaviour if the rest of the program runs into a bug that would normally be reported as SIGSEGV->program core dumps... Of course. I only write a glib example because there isn't a lot of space here. > (Also, mmap() isn't async-signal-safe in POSIX, so this is veering into OS-specifics and reliance on implementation details.) close() isn't thread-safe[1], so more people are in OS-specifics and relying on implementation details than we probably realise. I don't know of a single unixish system that gets mmap in a signal handler wrong. [1]: http://geocar.sdf1.org/close.html http://geocar.sdf1.org/close.html
- wahern 9y agoI once implemented a specialized contiguous stack structure that used this trick to automatically reallocate the stack. (The stack was state for a compiler-generated backtracking NFA.) The handler would longjmp to code that realloc'd the stack and restarted the NFA. (No internal pointers so resetting the NFA state and restarting was trivial.) I forget the exact performance improvement, but compared to the original code with explicit boundary checks I think it was several multiples faster. Unfortunately signal handlers are process-wide. These tricks are a bad idea for code that needs to play nice with other code (i.e. libraries, modules, etc) unless you know the process won't be multithreaded or you can own SIGSEGV. In the above hack I used sigaltstack, which is thread-local, to communicate per-thread state to the handler, allowing the scheme to work multi-threaded and to detect unintended faults. (The alt stack memory region used special page-aligned offsets so I could derive a thread-local object that didn't overlap with the signal stack.) Still, such a scheme is too ugly to hide inside a blackbox component which is why these tricks rightly don't see much usage. Your code and my code could have never run in the same process.
- jcranmer 9y ago> Unfortunately signal handlers are process-wide. These tricks are a bad idea for code that needs to play nice with other code (i.e. libraries, modules, etc) unless you know the process won't be multithreaded or you can own SIGSEGV. It's safe to handle processor trap-caused signals, since those are guaranteed to be delivered to the current thread. (SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGTRAP). The trickier part is that you can still send SIGSEGV and the like from another process, which means you have to distinguish between "is this signal caused by the processor or somebody else?" not to mention the general "is this signal in the code where I should handle it." Of course, a saner OS (like Windows) would give you a more stack-based approach to handling hardware traps, so that a function can say "if I, or anything I call, gets a #DE, deliver SIGFPE to this location."
- wahern 9y agoBut an installed signal handler is process-wide. There's no way for a thread to install a signal handler without potentially stepping on the toes of some other thread wanting to install its own handler. Contrast that with signal masks which are thread-local, permitting libraries to set and restore masks without disturbing global process state.
- MawKKe 9y agoAlso the last void* argument of the signal handler points to a ucontext_t object (man getcontext), so you can do something crazy like jumping over a NULL-dereference by modifying the instruction pointer and then "returning" with setcontext().
- deleted 9y ago[deleted]
- cryptonector 9y agoThis is all very slow on modern systems. I would not recommend it. But it's true that these are neat hacks!