4 ms·
Can I just call mmap instead of anything requiring userspace coordination beyond function scope if I need dynamic memory in a signal handler? How easy is it to
by ben0x539 10y ago
Can I just call mmap instead of anything requiring userspace coordination beyond function scope if I need dynamic memory in a signal handler?
How easy is it to fuck up the signalfd thing and end up not responding to SIGINT in a timely manner, because I'm being silly and doing some blocking work in the signalfd loop for some reason? Does that happen to people at all?
What kinda usecases are there for handling SIGSEGV? I'm not the kind of person to write on-demand-paging systems so I'm not really sure what that involves and when I need that sort of thing.
- mtanski 10y ago> What kinda usecases are there for handling SIGSEGV? If you're building a runtime that includes go like goroutines, you could handle SIGSEGV when you run out of stack space and graceful kill the go routine. You can use it for some kind of online migration of VMS. You could build a moving garbage collector system for native code multi-threaded code where you don't need to stop the world... you can deal with race of not stoping / moving by making those pages in accessible and only stoping the thread / fixing up if happens to hit moved memory. A runtime for a language that has multi-threading system that doesn't share VM mappings (which is possible using the clone sys call on linux) but can share other things. All somewhat advanced uses. But uses never the less.
- pcwalton 10y ago> What kinda usecases are there for handling SIGSEGV? Implementing a VM for Java or similar in which you need to trap null pointer accesses and turn them into exceptions is one use case.
- hornetblack 10y agoLast time I used C I set a SIGSEGV handler that printed a stack-trace and exited. Which made debugging somewhat easier. (Although It was often segfaulting in libwayland, which didn't help as much as I would like) SIGBUS is simialr to SIGSEGV (It happens if a mmap is larger that the backing file and you try to read the unbacked memory). You basically have to handle it if you don't control the backing file, Eg: with shared memory when the other process resizes it after you've mmap'd it. libwayland-server can install one for you that replaces the map with 0'd memory.
- mistralol 10y agoSIGSERV is basically a page fault in userspace. Its just named differently. The user mode linux kernel port makes use of it in exactly this way. Another use case would be a massive one to one hash table. You allocate the address space and when you fault on it you load data from a database on demand and continue execution.
- geofft 10y agoLinux also has userfaultfd for this, which I think is easier to program against: https://www.kernel.org/doc/Documentation/vm/userfaultfd.txt https://www.kernel.org/doc/Documentation/vm/userfaultfd.txt There's also something in Mach for this on OS X, but I forget the details. And a handful of non-UNIX OSes have had support for an outside program being able to get notified efficiently on segfaults and map pages in.
- gpderetta 10y ago> What kinda usecases are there for handling SIGSEGV? another one: attempt a best-effort flush of your log queue/buffer.
- geofft 10y agoDifficult, though! I'm not sure I know of any queue / buffer implementations that can be interrupted between any two arbitrary instructions and still preserve the buffer in a readable state. What happens if the buffer's updated capacity is written to memory before the updated pointer (remember that the compiler can reorder store instructions in the main program if it's not observable from other threads) and reading it causes another segfault? If you care deeply about reliable logging in the presence of arbitrary segfaults, get it to a buffer outside of the program: either write(2) / send(2) it to a file/pipe/socket, or write it to a shared memory region / mmaped file and pay attention to the atomicity and ordering of memory writes on that region. You might also conclude that arbitrary segfaults aren't in scope, and there's some specific high-risk code that is separate from your logging implementation. If you can prevent wild pointers in the high-risk code from reaching the pages used by the logging implementation (e.g., you're a language VM implementation or something), you can relax your requirements. In particular, if you can also prevent them from reaching the libc, and you know the high-risk code cannot induce a segfault in the libc, then you can probably start calling async-signal-unsafe functions. But this requires careful thought.
- gpderetta 10y agoAs I said, it is best effort :) On the logging system I'm familiar with, the logging queue is lock-free, so any invariant is preserved at every instruction boundary. The log is actually written to disk from a background thread, and the signal handler is responsible of signaling the log thread to flush the logging queue(s). Of course if the log thread itself has generated the sigsegv (because a log entry or the queue itself is corrupted by a bug) you are out of luck, but you are no worse than not attempting the flush. Agree that making the log thread a separate process instead is significantly more robust (and not significantly harder in fact).
- geofft 10y ago> Can I just call mmap instead of anything requiring userspace coordination beyond function scope if I need dynamic memory in a signal handler? Yes, but if you're doing nontrivial work, you probably don't just want memory for yourself, you want to share some actual program state with the main process. See the other comment I posted. In practice, making any raw system call is async-signal-sae. POSIX doesn't specify this because it doesn't specify what is a system call at all - it's purely a C-language, compile-time interface, and a unikernel that makes everything a library call, a Windows subsystem, etc. are all valid ways to implement it. But on a system where mmap is in fact a system call (and the libc wrapper does nothing interesting) it follows from first principles: there's no userspace resource that can be left in an inconsistent state. > How easy is it to fuck up the signalfd thing and end up not responding to SIGINT in a timely manner, because I'm being silly and doing some blocking work in the signalfd loop for some reason? Does that happen to people at all? I mean, this is basically the same question as "How easy is it for a system that's only accessible via an HTTP API to get stuck processing an event and not process a POST /quit". If you're worried about messing up your event loop and getting stuck, then yes, SIGINT interrupting your process is useful -- but also, you've already admitted you don't know what the state of your process is. So an orderly shutdown is difficult. Any work you could do to make it easier turns out to be work that makes your original non-blocking design get implemented correctly. You should probably just leave SIGINT at its default disposition (terminate the process immediately, same as SIGKILL) and save state to disk periodically or something. Or, at best, implement a signal handler that does very little interaction with process state, maybe just writing a log message (but note that syslog isn't async-signal-safe!) and exiting.