IL and Verification
IL is stack-based, which
means that all its instructions push operands onto an execution stack and pop
results off the stack. Because IL offers no instructions to manipulate
registers,compiler evelopers have an easy time producing IL code; they don’t
have to think about managing registers, and fewer IL instructions are needed
(since none exist for manipulating registers).
IL instructions are also
typeless. For example, IL offers an add instruction that adds the last in my
opinion, the biggest benefit of IL isn’t that it abstracts away the underlying
CPU. The biggest benefit is application robustness. While compiling IL into
native CPU instructions, the CLR performs a process called verification. Verification
examines the high-level IL code and ensures that everything it does is “safe.”
For example, verification checks that no memory is read from without having
previously been written to, that every method is called with the correct number
of parameters and that each parameter is of the correct type, that every two
operands pushed on the stack; there are not separate 32-bit and 64-bit add instructions.
When the add instruction
executes, it determines the types of the operands on the stack and performs the
appropriate operation. method’s return value is used properly, that every
method has a return tatement, and so on.
The managed module’s metadata includes all the method and type information used
by the verification process. If the IL code is determined to be “unsafe,” then
a System.Security.VerifierException exception is thrown, preventing the method
from executing.
Is Your Code Safe?
By default, the Microsoft
C# and Visual Basic compilers produce safe code. Safe code is code that is
verifiably safe. However, using the unsafe keyword in C# or other languages
(such as C++ with Managed Extensions or IL assembly language), it’s possible to
produce code that can’t be verifiably safe. The code might, in fact, be safe, but
verification is unable to prove this.
To ensure that all your
managed module’s methods contain verifiably safe IL, you can use the PEVerify
utility (PEVerify.exe) that ships with the .NET Framework SDK. When Microsoft
tests their C# and Visual Basic compilers, they run the resulting module through
PEVerify to ensure that the compiler always produces verifiably safe code. If PEVerify
detects unsafe code, Microsoft fixes the compiler.
You may want to consider
running PEVerify on your own modules before you package and ship them. If
PEVerify detects a problem, then there is a bug in the compiler and you should
report it to Microsoft (or whatever company produces the compiler you’re using).
If PEVerify doesn’t detect any unverifiable code, you know that your code will
run without throwing a VerifierException on the end-user’s machine.
You should be aware that
verification requires access to the metadata contained in any dependant
assemblies. So, when you use PEVerify to check an assembly, it must be able to
locate and load all referenced assemblies. Because PEVerify uses the CLR to locate
the dependant assemblies, the assemblies are located using the same binding and
probing rules that would normally be used when executing the assembly. (I’ll discuss
these binding and probing rules in Chapters
2 and 3.)
Note that an administrator
can elect to turn off verification (using the Microsoft .NET Framework
Configuration administrative tool). With verification off, the JIT compiler
will compile unverifiable IL into native CPU instructions; however, the
administrator is taking full responsibility for the code’s behavior.
In Windows, each process
has its own virtual address space. Separate address spaces are necessary
because you can’t trust the application’s code. It is entirely possible (and unfortunately,
all too common) that an application will read from or write to an invalid memory
address. By placing each Windows process in a separate address space, you gain robustness:
one process can’t adversely affect another process. By verifying the managed
code, however, you know that the code doesn’t improperly access memory that it
shouldn’t and you know that the code can’t adversely affect another
plication’s
code. This means that you can run multiple managed applications in a single
Windows virtual address
space.
Because Windows processes
require a lot of operating system resources, having many of them can hurt
performance and limit available resources. Reducing the number of processes by
running multiple applications in a single OS process can improve performance,
require fewer resources, and be just as robust. This is another benefit of
managed code as compared to unmanaged code.
The CLR does, in fact,
offer the ability to execute multiple managed applications in a single OS
process. Each managed application is called an AppDomain. By default, every
managed EXE will run in its own, separate address space that has just the one
AppDomain. However, a process hosting the CLR (such as Internet Information
Services [IIS] or a future version of SQL Server) can decide to run AppDomains
in a single OS process. I’ll devote part of Chapter
20 to a discussion of AppDomains.
0 nhận xét Blogger 0 Facebook
Post a Comment
Thank for you