Understanding User and Kernel Mode

Dave: of course you can read/write memory without API calls or interrupts, you just can’t write to kernel memory from usermode… whether memory mapped devices would probably be set as kernel.

mccoyn is probably thinking of the CPU feature called paging, included on x86 all the way back on the 80386. It doesn’t involve interrupts though, but does have some caching going on (TLB entries). Modern operating systems use it to provide a per-process view of memory space (CR3 register + paging tables), to translate these virtual addresses to physical addresses, and insulate processes against eachother. Complex topic that deserves a lot more text :slight_smile:

I think the choice is for the sake of having a uniform approach that guarantees a similar way of registering and invoking exception handlers in a way that is relatively independent of the language or architecture in use. It facilitates communicating first-chance and second-chance exceptions to the debugger in a uniform way; communicating failure to find an exception handler to the environment subsystem; invoking the unhandled exception filter; etc.

Debug vs. release builds won’t matter, and the various /EH types don’t matter either. I was surprised by this as well, but even if GCC is generally 4x faster at exception handling (and not just for this simple test), imho exception handling speed shouldn’t matter unless you’re abusing exceptions for something they weren’t made for (and considering that VC++ generally optimizes better than GCC, well…)

I threw together a little test package: http://f0dder.reteam.org/misc/throw_speed.zip

Interesting thing is that on 64bit XP, it seems like kernel32.RaiseException doesn’t go userkernel, but is still very slow (slower than 32bit with userkernel it would seem, but I don’t have XP32 on this box to test). Since testing was done with 32bit exe, it shouldn’t be because of extra registers needing to be saved, etc. With 64bit exe, things are bad.

32bit Vista, Turion-64-X2 TL-60 (2.00GHz)
throw_vc: 8174 ms kernel, 7316 ms user, 5000000 exceptions
throw_gcc: 0 ms kernel, 5350 ms user, 5000000 exceptions

64bit XP, AMD64x2 4400+ (2.21GHz)
throw_vc: 0 ms kernel, 7296 ms user, 5000000 exceptions
throw_gcc: 0 ms kernel, 2968 ms user, 5000000 exceptions
throw_x64: 0 ms kernel, 24437 ms user, 5000000 exceptions

Just got some more results…

64bit Vista, Intel Core 2 quad Q6600 2.4Ghz:
throw_gcc: 0 ms kernel, 4140 ms user, 5000000 exceptions
throw_vc: 16406 ms kernel, 23640 ms user, 5000000 exceptions
throw_x64: 0 ms kernel, 19421 ms user, 5000000 exceptions

keanuWhoa./keanu

I can confirm those values on my Vista 64 machine (Intel Core 2 Duo E6600, roughly the same results).

So SEH on 64-bit Vista is over four times slower than gcc’s exception mechanism, and going through the new WOW 32/64-bit translation layer doubles that time.

This is amazing. I’ll have to cook up test cases for C# and Java, though Java probably won’t give me kernel timing.

Chris: even if java won’t let you get kernel times, you can at least watch taskmanager or process explorer with “show kernel times” enabled, that’ll give you a good idea even if not accurate timings.

Amd Athlon XP 3000+ (32bit) running Linux kernel 2.6.23:

real 0m34.062s
user 0m33.784s
sys 0m0.202s

Which makes me wonder if I did something seriously wrong.

Operative words being “probably take a trip through the OS kernel.”

Precisely. “Probably” does not mean “always”.

Nowhere in this article does he state that either Windows SEH or the exception models built on top of it (which by the way include both .NET and Visual C++, as he explicitly says) will always go into kernel mode, as you and others were implying.

“Probably” should refer to the cause of the exception, or related processing, when an exception occurs in the context of I/O. I would be extremely surprised if an Int32.Parse exception did in fact transition to kernel mode.

Note: it seems the Visual C++ runtimes do call kernel32.RaiseException wich calls ntdll.RtlRaiseException which calls ntdll.ZwRaiseException, which eventually does do a switch to ring0.

Did you trace the code path for all exception handling, or did you just look up the referenced API calls?

The VC++ runtime might well switch to Ring 0 for some exceptions but that doesn’t mean this happens for all exceptions.

PS Conjecture: This code path that calls ZwRaiseException might be executed only when an exception is unhandled by user code, so as to allow the Windows exception handler or an attached debugger to deal with it.

Addendum: I thought that maybe JIT inlining made a difference, so I tried manually inlining the throw statement, and disabling optimization to disable automatic inlining of the “return new Exception()” statement. Once again, no difference in the results.

Please don’t run amok and think exceptions are superbad and should be avoided just because of this, the trick is to use them for exceptional conditions, and not to abuse them for general control flow manipulation (and obviously not for tight loops ;)).

Sure, I know. I’m no Joel Spolsky, I’ll keep using exceptions for error handling. :slight_smile:

Unfortunately, I did abuse .NET exceptions occasionally for regular flow control because I thought they would be fast enough to handle rarely visited branches. I’ll have to go back and check all those cases, and probably rewrite them.

Well I know it’s not my code that’s to blame for these exceptions. It’s those darn end users doing things they shouldn’t do!

Chris Nahr: a thing I wouldn’t call misuse, btw, would be breaking out of deep nesting (whether it be just control structures or function calls as well), instead of using goto (which only works for local nesting) or propagating returncodes for many layers.

But again, only if the abort is done rarely (or, should I say, “exceptionally” :)).

az: catch(…) catches SEH exceptions in VC because VC C++ exceptions are modeled ontop of SEH exceptions (thus the userkernel roundtrips in some windows versions).

GCC code won’t catch SEH exceptions with catch(…), which also means you need SetUnhandledExceptionFilter (or local SEH try/catch blocks, dunno if GCC supports that?) if you want to catch hardware exceptions or other SEH exceptions from the few API calls that can raise them.

The green line is total CPU time; the red line is Kernel time. The gap between the two is User time.

Not exactly.

Total CPU time means wall clock time, which is the total time the program took to run. CPU time, as you said, is the time that the CPU was running in level 0 (kernel mode) and User time is the time that the process was executing (ie. using the CPU) in level 3 (user mode).

There is a huge difference of User mode and total time because the process can be waiting for a resource and not use the CPU at all. To clarify, check the example below:

  1. Create a FIFO:
    $ mkfifo foo

  2. ‘cat’ the fifo (ie. read from it)
    $ time cat foo

It’ll block, waiting from data or EOF, but there’s nothing writing to the FIFO so it’ll wait forever, without a single User mode CPU utilization.

  1. In another terminal write something to the FIFO:
    $ echo “foo” foo

The first terminal will then write “foo” and exit (echo sends EOF). The time reported will be something like this:

real 0m12.239s
user 0m0.000s
sys 0m0.008s

Which means it spent 0.008 seconds to send the output throughout the FIFO, 0 seconds in user mode and 12.24 seconds waiting.

Renato: actually Jeff’s statement is correct, since taskmgr and process explorer show the current CPU usage/load, NOT Walltime. Same thing if you use the per-process “performance graph” of Process Explorer. If you have an app that’s doing a blocking wait (and has no other active threads), it’s CPU usage graph will flatline.

When talking about absolute time usage, you’re obviously right that we’re talking Walltime and CPUTime where CPUTime is split between kernel and user. But task managers don’t show absolute usage in their graph overview, they show relative CPU usage since last update-tick.

great informative comments.
one interesting detail which wasn’t mentioned so far is that, at least in VC genreated code, catch(…) will truly catch all: both C++ and SEH.

which is, of course, a good enough reason to never ever use catch(…) :wink:

“rim’s example will make your process run with SYSTEM credentials and will thus give you complete control of the system (SYSTEM has more privileges than any Administrator), but it doesn’t mean you’ll run in kernel mode”

Not only not kernel mode (which would as he said be no use to you anyway) but also not a true admin either it does have some elevated privileges (and full control of most files) but it is limited in some subtle ways, the main difference is that it is a local only (no privileges on the network) and service only (no interactive logon) which is why you have to trick the system to run cmd

Jeff, Could you tell me , how come there are two displays in your pic of the CPU Usage History ? If i open my Task Manager it shows only one box of graph for CPU Usage History.

Jaster: true, the trick doesn’t grant you any network privs, you only get full (local) privileges - like being able to look inside “System Volume Information” :slight_smile:

Sri: because Jeff has a dual-core machine.

Jonathan Wilson: ouch, no SEH support at all, compiler side? You can always resort to SetUnhandledExceptionFilter though, and you could add some SEH support with assembly, but that’d be messy.