At the Ruby level, there are two procedures that can be used for
loading: require
and load
.
require 'uri' # load the uri library load '/home/foo/.myrc' # read a resource file
They are both normal methods, compiled and evaluated exactly like any other code. It means loading occurs after compilation gave control to the evaluation stage.
These two function each have their own use. ‘require’ is to load
libraries, and load
is to load an arbitrary file. Let’s see this in
more details.
require
require
has four features:
.rb
/.so
extension can be omittedRuby’s load path is in the global variable $:
that contains an
array of strings. For example, displaying the content of the $:
in
the environment I usually use would show:
% ruby -e 'puts $:' /usr/lib/ruby/site_ruby/1.7 /usr/lib/ruby/site_ruby/1.7/i686-linux /usr/lib/ruby/site_ruby /usr/lib/ruby/1.7 /usr/lib/ruby/1.7/i686-linux .
Calling puts
on an array displays one element by line so it’s easy
to read.
As I ran configure
using --prefix=/usr
, the library path is
/usr/lib/ruby
and below, but if you compile it normally from the
source code, the libraries will be in /usr/local/lib/ruby
and below.
In a Windows environment, there will also be a drive letter.
Then, let’s try to require
the standard library nkf.so
from the
load path.
require 'nkf'
If the require
d name has no extension, require
silently
compensates. First, it tries with .rb
, then with .so
. On some
platforms it also tries the platform’s specific extension for
extension libraries, for example .dll
in a Windows environment or
.bundle
on Mac OS X.
Let’s do a simulation on my environment. ruby
checks the following
paths in sequential order.
/usr/lib/ruby/site_ruby/1.7/nkf.rb /usr/lib/ruby/site_ruby/1.7/nkf.so /usr/lib/ruby/site_ruby/1.7/i686-linux/nkf.rb /usr/lib/ruby/site_ruby/1.7/i686-linux/nkf.so /usr/lib/ruby/site_ruby/nkf.rb /usr/lib/ruby/site_ruby/nkf.so /usr/lib/ruby/1.7/nkf.rb /usr/lib/ruby/1.7/nkf.so /usr/lib/ruby/1.7/i686-linux/nkf.rb /usr/lib/ruby/1.7/i686-linux/nkf.so found!
nkf.so
has been found in /usr/lib/ruby/1.7/i686-linux
. Once the
file has been found, require
’s last feature (not loading the file
more than once) locks the file. The locks are strings put in the
global variable $"
. In our case the string "nkf.so"
has been put
there. Even if the extension has been omitted when calling require
,
the file name in $"
has the extension.
require 'nkf' # after loading nkf... p $" # ["nkf.so"] the file is locked require 'nkf' # nothing happens if we require it again p $" # ["nkf.so"] the content of the lock array # does not change
The are two reasons for adding the missing extension. The first one is
not to load it twice if the same file is later require
d with its
extension. The second one is to be able to load both nkf.rb
and
nkf.so
. In fact the extensions are disparate (.so .dll .bundle
etc.) depending of the platform, but at locking time they all become
.so
. That’s why when writing a Ruby program you can ignore the
differences of extensions and consider it’s always so
. So you can
say that ruby
is quite UNIX oriented.
By the way, $"
can be freely modified even at the Ruby level so we
cannot say it’s a strong lock. You can for example load an extension
library multiple times if you clear $"
.
load
load
is a lot easier than require
. Like require
, it searches the
file in $:
. But it can only load Ruby programs. Furthermore, the
extension cannot be omitted: the complete file name must always be
given.
load 'uri.rb' # load the URI library that is part of # the standard library
In this simple example we try to load a library, but the proper way to
use load
is for example to load a resource file giving its full
path.
If we roughly split it, “loading a file” can be split in:
The only difference between require
and load
is how to find the
file. The rest is the same in both.
We will develop the last evaluation part a little more. Loaded Ruby programs are basically evaluated at the top-level. It means the defined constants will be top-level constants and the defined methods will be function-style methods.
### mylib.rb MY_OBJECT = Object.new def my_p(obj) p obj end ### first.rb require 'mylib' my_p MY_OBJECT # we can use the constants and methods defined # in an other file
Only the local variable scope of the top-level changes when the file
changes. In other words, local variables cannot be shared between
different files. You can of course share them using for example Proc
but this has nothing to do with the load mechanism.
Some people also misunderstand the loading mechanism. Whatever the
class you are in when you call load
, it does not change
anything. Even if, like in the following example, you load a file in
the module
statement, it does not serve any purpose, as everything
that is at the top-level of the loaded file is put at the Ruby
top-level.
require 'mylib' # whatever the place you require from, be it # at the top-level module SandBox require 'mylib' # or in a module, the result is the same end
Here the mechanism is a lot about details, so it’s a little difficult to enumerate it simply. That’s why we will work a little differently on it, and we are going to reduce the target to 3 points:
Regarding the first point, you will understand it when you see it.
For the second point, the functions that appear in this chapter come
from 4 different files, eval.c ruby.c file.c dln.c
. We’ll look at
the reason they are stretched in different places.
The third point is just like its name says. We will see how works the currently popular trend of execution time loading, more commonly referred to as plug-ins. This is the most important part of this chapter so I’d like to use as many pages as possible to talk about it.
rb_f_require()
The body of require
is rb_f_require
. First, we will only look at
the part concerning the file search. Having many different cases is
bothersome so we will limit ourselves to the case when no file
extension is given.
rb_f_require()
(simplified version)
5527 VALUE 5528 rb_f_require(obj, fname) 5529 VALUE obj, fname; 5530 { 5531 VALUE feature, tmp; 5532 char *ext, *ftptr; /* OK */ 5533 int state; 5534 volatile int safe = ruby_safe_level; 5535 5536 SafeStringValue(fname); 5537 ext = strrchr(RSTRING(fname)->ptr, '.'); 5538 if (ext) { /* ...if the file extension has been given... */ 5584 } 5585 tmp = fname; 5586 switch (rb_find_file_ext(&tmp, loadable_ext)) { 5587 case 0: 5588 break; 5589 5590 case 1: 5591 feature = fname = tmp; 5592 goto load_rb; 5593 5594 default: 5595 feature = tmp; 5596 fname = rb_find_file(tmp); 5597 goto load_dyna; 5598 } 5599 if (rb_feature_p(RSTRING(fname)->ptr, Qfalse)) 5600 return Qfalse; 5601 rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr); 5602 5603 load_dyna: /* ...load an extension library... */ 5623 return Qtrue; 5624 5625 load_rb: /* ...load a Ruby program... */ 5648 return Qtrue; 5649 } 5491 static const char *const loadable_ext[] = { 5492 ".rb", DLEXT, /* DLEXT=".so", ".dll", ".bundle"... */ 5493 #ifdef DLEXT2 5494 DLEXT2, /* DLEXT2=".dll" on Cygwin, MinGW */ 5495 #endif 5496 0 5497 }; (eval.c)
In this function the goto
labels load_rb
and load_dyna
are
actually like subroutines, and the two variables feature
and fname
are more or less their parameters. These variables have the following
meaning.
variable | meaning | example |
feature |
the library file name that will be put in $" |
uri.rb 、nkf.so |
fname |
the full path to the library | /usr/lib/ruby/1.7/uri.rb |
The name feature
can be found in the function rb_feature_p()
. This
function checks if a file has been locked (we will look at it just
after).
The functions actually searching for the library are rb_find_file()
and rb_find_file_ext()
. rb_find_file()
searches a file in the load
path $'
. rb_find_file_ext()
does the same but the difference is
that it takes as a second parameter a list of extensions
(i.e. loadable_ext
) and tries them in sequential order.
Below we will first look entirely at the file searching code, then we
will look at the code of the require
lock in load_rb
.
rb_find_file()
First the file search continues in rb_find_file()
. This function
searches the file path
in the global load path $'
(rb_load_path
). The string contamination check is tiresome so we’ll
only look at the main part.
rb_find_file()
(simplified version)
2494 VALUE 2495 rb_find_file(path) 2496 VALUE path; 2497 { 2498 VALUE tmp; 2499 char *f = RSTRING(path)->ptr; 2500 char *lpath; 2530 if (rb_load_path) { 2531 long i; 2532 2533 Check_Type(rb_load_path, T_ARRAY); 2534 tmp = rb_ary_new(); 2535 for (i=0;i<RARRAY(rb_load_path)->len;i++) { 2536 VALUE str = RARRAY(rb_load_path)->ptr[i]; 2537 SafeStringValue(str); 2538 if (RSTRING(str)->len > 0) { 2539 rb_ary_push(tmp, str); 2540 } 2541 } 2542 tmp = rb_ary_join(tmp, rb_str_new2(PATH_SEP)); 2543 if (RSTRING(tmp)->len == 0) { 2544 lpath = 0; 2545 } 2546 else { 2547 lpath = RSTRING(tmp)->ptr; 2551 } 2552 } 2560 f = dln_find_file(f, lpath); 2561 if (file_load_ok(f)) { 2562 return rb_str_new2(f); 2563 } 2564 return 0; 2565 } (file.c)
If we write what happens in Ruby we get the following:
tmp = [] # make an array $:.each do |path| # repeat on each element of the load path tmp.push path if path.length > 0 # check the path and push it end lpath = tmp.join(PATH_SEP) # concatenate all elements in one # string separated by PATH_SEP dln_find_file(f, lpath) # main processing
PATH_SEP
is the path separator
: ':'
under UNIX, ';'
under
Windows. rb_ary_join()
creates a string by putting it between the
different elements. In other words, the load path that had become an
array is back to a string with a separator.
Why? It’s only because dln_find_file()
takes the paths as a string
with PATH_SEP
as a separator. But why is dln_find_file()
implemented like that? It’s just because dln.c
is not a library for
ruby
. Even if it has been written by the same author, it’s a general
purpose library. That’s precisely for this reason that when I sorted
the files by category in the Introduction I put this file in the
Utility category. General purpose libraries cannot receive Ruby
objects as parameters or read ruby
global variables.
dln_find_file()
also expands for example ~
to the home directory,
but in fact this is already done in the omitted part of
rb_find_file()
. So in ruby
’s case it’s not necessary.
Here, file search is finished quickly. Then comes is the loading
code. Or more accurately, it is “up to just before the load”. The code
of rb_f_require()
’s load_rb
has been put below.
rb_f_require():load_rb
5625 load_rb: 5626 if (rb_feature_p(RSTRING(feature)->ptr, Qtrue)) 5627 return Qfalse; 5628 ruby_safe_level = 0; 5629 rb_provide_feature(feature); 5630 /* the loading of Ruby programs is serialised */ 5631 if (!loading_tbl) { 5632 loading_tbl = st_init_strtable(); 5633 } 5634 /* partial state */ 5635 ftptr = ruby_strdup(RSTRING(feature)->ptr); 5636 st_insert(loading_tbl, ftptr, curr_thread); /* ...load the Ruby program and evaluate it... */ 5643 st_delete(loading_tbl, &ftptr, 0); /* loading done */ 5644 free(ftptr); 5645 ruby_safe_level = safe; (eval.c)
Like mentioned above, rb_feature_p()
checks if a lock has been put
in $"
. And rb_provide_feature()
pushes a string in $"
, in other
words locks the file.
The problem comes after. Like the comment says “the loading of Ruby programs is serialised”. In other words, a file can only be loaded from one thread, and if during the loading a thread tries to load the same file, that thread will wait for the first loading to be finished. If it were not the case:
Thread.fork { require 'foo' # At the beginning of require, foo.rb is # added to $" } # However the thread changes during the evaluation # of foo.rb require 'foo' # foo.rb is already in $" so the function returns # immediately # (A) the classes of foo are used...
By doing something like this, even though the foo
library is not
really loaded, the code at (A) ends up being executed.
The process to enter the waiting state is simple. A st_table
is
created in loading_tbl
, the association “feature=>
waiting thread”
is recorded in it. curr_thread
is in eval.c
’s functions, its value
is the current running thread.
The mechanism to enter the waiting state is very simple. A st_table
is created in the loading_tbl
global variable, and a
“feature
=>loading thread
” association is created. curr_thread
is
a variable from eval.c
, and its value is the currently running
thread. That makes an exclusive lock. And in rb_feature_p()
, we
wait for the loading thread to end like the following.
rb_feature_p()
(second half)
5477 rb_thread_t th; 5478 5479 while (st_lookup(loading_tbl, f, &th)) { 5480 if (th == curr_thread) { 5481 return Qtrue; 5482 } 5483 CHECK_INTS; 5484 rb_thread_schedule(); 5485 } (eval.c)
When rb_thread_schedule()
is called, the control is transferred to
an other thread, and this function only returns after the control
returned back to the thread where it was called. When the file name
disappears from loading_tbl
, the loading is finished so the function
can end. The curr_thread
check is not to lock itself (figure 1).
Figure 1: Serialisation of loads
rb_load()
We will now look at the loading process itself. Let’s start by the
part inside rb_f_require()
’s load_rb
loading Ruby programs.
rb_f_require()-load_rb-
loading
5638 PUSH_TAG(PROT_NONE); 5639 if ((state = EXEC_TAG()) == 0) { 5640 rb_load(fname, 0); 5641 } 5642 POP_TAG(); (eval.c)
Here the rb_load()
that is called is in fact the real form of the
Ruby level load.
And rb_load ()
, this that are calling it here are the substance of load
of a Ruby level actually.
I see the same work 1 time, with reason that a/the search becomes necessary another time to say that it is not able to have already done it how. Thereupon, in the following, the part has been omitted.
Because even, wrap
of the 2nd argument is 0 with the above calling cord also folding it with 0 it has been crowded.
rb_load()
(simplified edition)
void rb_load(fname, /* wrap=0 */) VALUE fname; { int state; volatile ID last_func; volatile VALUE wrapper = 0; volatile VALUE self = ruby_top_self; NODE *saved_cref = ruby_cref; PUSH_VARS(); PUSH_CLASS(); ruby_class = rb_cObject; ruby_cref = top_cref; /* (A-1) CREF It changes it */ wrapper = ruby_wrapper; ruby_wrapper = 0; PUSH_FRAME(); ruby_frame->last_func = 0; ruby_frame->last_class = 0; ruby_frame->self = self; /*(A-2) ruby_frame->cbase changes it */ ruby_frame->cbase=(VALUE)rb_node_newnode(NODE_CREF,ruby_class,0,0); PUSH_SCOPE(); /* at the top-level the visibility is private by default */ SCOPE_SET(SCOPE_PRIVATE); PUSH_TAG(PROT_NONE); ruby_errinfo = Qnil; /* make sure it's nil */ state = EXEC_TAG(); last_func = ruby_frame->last_func; if (state == 0) { NODE *node; /* (B) Why or the same handling as eval */ ruby_in_eval++; rb_load_file(RSTRING(fname)->ptr); ruby_in_eval--; node = ruby_eval_tree; if (ruby_nerrs == 0) { /* no parse error occurred */ eval_node(self, node); } } ruby_frame->last_func = last_func; POP_TAG(); ruby_cref = saved_cref; POP_SCOPE(); POP_FRAME(); POP_CLASS(); POP_VARS(); ruby_wrapper = wrapper; if (ruby_nerrs > 0) { /* a parse error occurred */ ruby_nerrs = 0; rb_exc_raise(ruby_errinfo); } if (state) jump_tag_but_local_jump(state); if (!NIL_P(ruby_errinfo)) /* an exception was raised during */ /* the loading */ rb_exc_raise(ruby_errinfo); }
I thought that it was able to come off from the storm of stack operation finally instantaneous and also even that rushes there is a mentally painful thing, be going to read and be going to cure be going to take a/the feeling.
It is the constant of a long function and most of the cords are occupied with an/the idiom.
It jumps with PUSH
/ POP
, tag protect. It is the CREF
relation of (A) to want to pay attention even the middle. Because it is always implemented on a/the big league ruby_cref
the program that loaded it is not) (a push it evacuates and return it to top_cref
.
Even ruby_frame->cbase
is making a new thing.
With it and 1 place, (B) it has already made why or ruby_inch_eval
When this variable tries to check what influence on earth in the first place the function called rb_compile_error ()
only it is as. Outputting a/the message to
is real it is that. S/he seems to plan and seem to say that I stop it because it is poor in an/the evaluation device although I want to output it to stderr
when preserve a/the message to an/the exception object and be nonexistent so when ruby_inch_eval stderr
suddenly at the time of the perspective drawing error of the main program of a/the command namely. Then eval of ruby_inch_eval
is not method eval
and function eval ()
and evaluate of a general verb or it may indicate about and also eval. c
.
rb_load_file()
Here a/the sauce file suddenly moves to ruby. c. I say that is not an actual place this way even more? Namely, the file of load relation wants to put it to ruby. c basically. However, I have to use etc. PUSH_TAG () in rb_load (). Therefore it puts it to eval. c unwillingly. It will put it to all the eval. c from the beginning if it is not.
It is rb_load_file ()
with it.
rb_load_file()
865 void 866 rb_load_file(fname) 867 char *fname; 868 { 869 load_file(fname, 0); 870 } (ruby.c)
Each circle transfer. (Load_file) of the 2nd argument script shows whether or not that be a truth or falsehood value and be loading the file of the argument of a/the ruby command. Observing ?? with script=0 because be not so now and want to think the load of a/the library it will be crowded. Furthermore the one that even a/the meaning thinks in the following, and be not essential has been shaved.
▼load_file()
(simplified edition)
static void load_file(fname, /* script=0 */) char *fname; { VALUE f; { FILE *fp = fopen(fname, "r"); (A) if (fp == NULL) { rb_load_fail(fname); } fclose(fp); } f = rb_file_open(fname, "r"); (B) rb_compile_file(fname, f, 1); (C) rb_io_close(f); }
(A) In practice, the try to open using fopen()
is to check if the
file can be opened. If there is no problem, it’s immediately closed.
It may seem a little useless but it’s an extremely simple and yet
highly portable and reliable way to do it.
(B) The file is opened once again, this time using the Ruby level
library File.open
. The file was not opened with File.open
from the
beginning not to raise any Ruby exception if the file cannot be
opened. Here if any exception occurred we would like to have a
loading error, but getting the errors related to open
, for example
Errno::ENOENT
, Errno::EACCESS
..., would be problematic. We are in
ruby.c
so we cannot stop a tag jump.
(C) Using the parser interface rb_compile_file()
, the program is
read from an IO
object, and compiled in a syntax tree. The syntax
tree is added to ruby_eval_tree
so there is no need to get the
result.
That’s all for the loading code. Finally, the calls were quite deep so
let’s look at the callgraph of rb_f_require()
bellow.
rb_f_require ....eval.c rb_find_file ....file.c dln_find_file ....dln.c dln_find_file_1 rb_load rb_load_file ....ruby.c load_file rb_compile_file ....parse.y eval_node
We’ve seen a lot of callgraphs, they are now common sense.
open
required for loadingLike we’ve seen before, there are open
used just to check if a file
can be open, but in fact during the loading process other functions
like for example rb_find_file_ext()
also do checks using open
. How
many times is open()
called in the whole process?
It is the way of a correct programmer to try to count actually if I think that. It is counted easily if I employ a/the system call tracer. The tool for that should be found right away if it searches it with Google, although a/the name is scattering with ?? by OS as it says with ktrace or truss, if it is truss, BSD system if it is strace, Solaris if it is Linux. A/the tracer is attached to IDE usually if it is Windows.
Well, as my main environment is Linux, I looked using strace
.
The output is done on stderr
so it was redirected using 2>&1
.
% strace ruby -e 'require "rational"' 2>&1 | grep '^open' open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT open("/etc/ld.so.cache", O_RDONLY) = 3 open("/usr/lib/libruby-1.7.so.1.7", O_RDONLY) = 3 open("/lib/libdl.so.2", O_RDONLY) = 3 open("/lib/libcrypt.so.1", O_RDONLY) = 3 open("/lib/libc.so.6", O_RDONLY) = 3 open("/usr/lib/ruby/1.7/rational.rb", O_RDONLY|O_LARGEFILE) = 3 open("/usr/lib/ruby/1.7/rational.rb", O_RDONLY|O_LARGEFILE) = 3 open("/usr/lib/ruby/1.7/rational.rb", O_RDONLY|O_LARGEFILE) = 3 open("/usr/lib/ruby/1.7/rational.rb", O_RDONLY|O_LARGEFILE) = 3
Because it is open ?? that I is using with real ?? of a/the dynamic link to open of libc.so.6 remaining open be 4 times a/the total. Three times seem to be useless namely.
rb_f_require()
-load_dyna
This time we will see the loading of extension libraries. We will
start with rb_f_require()
’s load_dyna
. However, we do not need the
part about locking anymore so it was removed.
rb_f_require()
-load_dyna
5607 { 5608 int volatile old_vmode = scope_vmode; 5609 5610 PUSH_TAG(PROT_NONE); 5611 if ((state = EXEC_TAG()) == 0) { 5612 void *handle; 5613 5614 SCOPE_SET(SCOPE_PUBLIC); 5615 handle = dln_load(RSTRING(fname)->ptr); 5616 rb_ary_push(ruby_dln_librefs, LONG2NUM((long)handle)); 5617 } 5618 POP_TAG(); 5619 SCOPE_SET(old_vmode); 5620 } 5621 if (state) JUMP_TAG(state); (eval.c)
There is not an almost novel thing now. The messenger the same as an/the idiom or the tag is the method that is not making reliable and even the evacuation/return of the visibility nature scope got used to seeing. Only dln_load) (is to remain. This may be doing what on earth. Where it says that next it continues.
Although dln_load () is why be loading an/the expansion library how is it that says that an/the expansion library is loaded? First of all a/the conversation is wound toward the physics world bravely to talk it and return and must start from a/the link.
Needless to say I think that it is that the program of C is compiled. The program that moves is able to make it if it does it in the following manner, because an/the author is using gcc with Linux.
% gcc hello.c
It is Hello, World this probably when it does it from a/the file name! It may be a program. I am able to implement it in the following manner subsequently because gcc outputs a/the program in the file called a. out with default in UNIX.
% ./a.out Hello, World!
Properly it is prepared.
By the way, as for gcc what did it do in fact now? Although there are many cases I say compiling, compiling usually in fact /p>
cpp
) cc
) as
) ld
)It is passing four stages that say that. Seem to there are many cases only the stage of a/the link ends why or without being turned an/the express statement, although I see explanation in various place to ?? compiling assembling among these. Do not reach to "a/the present" absolutely in the lesson of the history of a/the school and be such that and similar thing? Thereupon, I will summarize easily what a/the link is first of all, to bury the extinction in this book.
The program that the stage until assembling completed is becoming "the object file" of some sort of form. There is something like the following in a major thing with the form like that.
A. out
,assembler output (comparatively old UNIX)Although they say to make sure a. out of the default output file name of a. out and cc of the object file form are an exception at all. For example file a. out of an/the ELF form is produced if I make it usually with Linux in nowadays.
With it, the such conversation that this object file form differs how may this how be い!. "The gathering of a/the name" that even which is conceivable, to must recognize now and these object files are that. For example it is the function name and variable name etc. that exist in this file.
there are 2 kinds in the gathering of the name that is included in an/the object file. Namely
printf
) hello
)With it is. And, confirming that "the gatherings of the necessary name" of all object file are included in" the gathering of the name that "offers when the link has collected a plural object file it is to connect mutually. It is (Figure 2) that must do it as the object file of somewhere is linked to" the name that pulls a/the line from all "a necessary name" namely and "offer. (Resolving undefined symbol) that solves an/the undefinition symbol, if I say this case by using terminology, and it becomes.
Figure 2: Object file and link
The program is why it does not run to that extent really, although it is that it says logically so. At least the program of C does not run. It is because I am not able to move if it does not have a/the name into an/the address (a/the number) transformed.
Thereupon, a physical connection becomes necessary next to a logical connection. If map an/the object file in real memory space and do not replace all the names with a/the number I do not go. Concretely speaking calling a/the function the jump tip address of when is adjusted.
nd depending as if these two connection are done when a/the link is divided into 2 kinds. It is a static link and dynamic link namely. A/the static link has ended all the stages at the time of compiling. On the other hand a/the dynamic link delays a little to at the time of the implementation of a/the program the house of a/the connection. And becoming at the time of the implementation of a program a/the link completes for the first time.
There is the face that is a very simple ideal model that I explained it most here and be distorting reality fairly. A/the logic connection and physics connection are divided the gathering of a/the name "an/the object file" that nonexistent, says so flatly and it passes naively even to. However, anyway another book this hit has got able to write if it is speaking seriously, because an/the action differs too much by a/the platform. I am sufficient to read it even to every {'Linkers &Loaders』John 'Linkers &Loaders』John R. Levine ©, Sakakihara 1 arrow © translation positive edge translation, ohm company, 2001 'expert C programming' \ footnote 'expert C programming' Peter van der Linden ©, Umehara system translation, ASCII publication office, 1996 'Linkers &Loaders' ©footnote more to obtain the knowledge of a real level.And let's enter into the main question by now. Quite a lot parts are actually decided at the time of compiling when it is a dynamic link the that say usually, although "the dynamic" of dynamic link is the meaning that naturally does" at the time of "implementation. For example the name of a necessary function does will be decided and even in the library of where that is has already understood. For example it is why it links with the feeling called gcc-lm because it is in libm if it is) (cos. If I do not designate it at the time of compiling it becomes a link error.
However, it differs in the case of an/the expansion library. Even even the name of the library that even, the name of a necessary function links is not decided at the time of compiling. Assembling a/the letter line to during execution of a/the program it must link load. Even even "the logic connection" that says in the word in a while ago namely I must do it at the time of all the implementation. The mechanism that also a/the dynamic link differs a little the that say usually for that becomes necessary.
Link, of a/the case that decides all at the time of implementation namely these operation, is called "a dynamic load (dynamic load)" usually. It makes "a dynamic load" with a/the Chinese character particularly because it is confusing when it is a dynamic link and dynamic load, although it will should open to "a/the dynamic load" and Katakana when I go from the terminology messenger of this book.
Of the explanation is. It is whether or not it is good if the dynamic load is done how after. Even if it says that it is not difficult and this is good if it is called only because exclusive use API is available to a/the system usually.
For example that is in a wide area comparatively if it is UNIX is API called dlopen. Yet it is not said to that it is if it is "UNIX. For example I use NeXT style API when there is the interface that differs to HP-UX of before entirely a little bit and is Mac OS X. By the time be on credit as libdl when it is Linux although it is in libc when be a BSD system also and even same dlopen is gallant outside etc. etc., there is not transplantation nature. It is natural even to differ at all if it becomes other OS, because it differs only this even if be and be claimed to be together with a/the UNIX system. First of all same API is impossible to be used.
Thereupon, when it says how it is doing it ruby is preparing the file called dln. c the to absorb the interface that differs at all. Dln may be the omission of dynamic link. Dln_load) (is one of the function of the dln. c.
The thing help is that the use pattern of API is same entirely at least, although it is to such a way dynamic load API of scattering entirely. 《主語なし》When it may be which platform
という三段階で構成されている。例えばdlopen
系APIならば
dlopen
dlsym
dlclose
However it corresponds. If it is Win32 API
LoadLibrary
(or LoadLibraryEx
)GetProcAddress
FreeLibrary
However it corresponds
Lastly, using this API group, you probably will speak dln_load () does what.This to tell the truth, is the call of Init_xxxx ().Whole process to end defect does not fall finally from ruby starting reaching up to here and reaches the point where you can draw.Namely, ruby when it starts, initializes the evaluator and starts the appraisal of the main program which is received with a some method.When the midway require or load happens, the library is loaded and control is moved.Control is moved, if with, it is the Ruby library, pass doing, it is to appraise,
dln_load()
Finally, it could arrive to the content of dln_load ().But the function whose also dln_load () is long, this and there being a reason, structure is simple.First we want looking at approximate shape.
▼dln_load()
(Approximate shape)
void* dln_load(file) const char *file; { #if defined _WIN32 && !defined __CYGWIN__ Win32 API So it loads #else Initialization #ifdef each platform of platform independence every of platform ..... Routine ...... #endif #endif #if !defined(_AIX) && !defined(NeXT) failed: rb_loaderror("%s - %s", error, file); #endif return 0; /* dummy return */ }
It is good if I am thinking only one platform one when I think, because the part that becomes a main like this is separating perfectly every a/the platform. API that is supported is as follows.
dlopen
(UNIX)LoadLibrary
(Win32)shl_load
(HP-UX)a.out
(UNIX)rld_load
(NeXT4
)dyld
(NeXT
Mac OS X)get_image_symbol
(BeOS)GetDiskFragment
(Mac OS 9load
(AIX)dln_load()
-dlopen()
First of all let's go from the cord of API of a/the dlopen system.
▼dln_load()
-dlopen()
1254 void* 1255 dln_load(file) 1256 const char *file; 1257 { 1259 const char *error = 0; 1260 #define DLN_ERROR() (error = dln_strerror(),\ strcpy(ALLOCA_N(char, strlen(error) + 1), error)) 1298 char *buf; 1299 /* Init_xxxx is written to buf, (as for */ /* territory alloca to allot,) */ 1300 init_funcname(&buf, file); 1304 { 1305 void *handle; 1306 void (*init_fct)(); 1307 1308 #ifndef RTLD_LAZY 1309 # define RTLD_LAZY 1 1310 #endif 1311 #ifndef RTLD_GLOBAL 1312 # define RTLD_GLOBAL 0 1313 #endif 1314 1315 /* (A)Loading the library */ 1316 if ((handle=(void*)dlopen(file, RTLD_LAZY | RTLD_GLOBAL)) == NULL) { 1317 error = dln_strerror(); 1318 goto failed; 1319 } 1320 /* (B)Init_xxxx()To the pointer is taken */ 1321 init_fct = (void(*)())dlsym(handle, buf); 1322 if (init_fct == NULL) { 1323 error = DLN_ERROR(); 1324 dlclose(handle); 1325 goto failed; 1326 } 1327 /* (C)Init_xxxx()It calls */ 1328 (*init_fct)(); 1329 1330 return handle; 1331 } 1576 failed: 1577 rb_loaderror("%s - %s", error, file); 1580 } (dln.c)
(A)When dlopen () of RTLD_LAZY of an/the argument demanded a/the function "actually to solve" an/the unresolved symbol is shown. It returns and, with such a stamp (the handle) that distinguishes a/the library a/the dl*() to a/the value does not go if it always does not gives this.
(B)dlsym) (takes a/the function pointer from the library where handle shows. 《If returns and the value be NULL it is a failure. Here I take, call a/the pointer to Init_xxxx ().
dlclose()
poor because the whole library has come not to use it when it does dlclose (), although I should be returning the function pointer of the library where it loaded in) (Init_xxxx. Until a/the process ends namely dlclose) (is not able to call it.
dln_load()
-Win32I use LoadLibrary () and GetProcAddress () in Win32. It is very general Win32 API that is appearing in MSDN.
▼dln_load()
-Win32
1254 void* 1255 dln_load(file) 1256 const char *file; 1257 { 1264 HINSTANCE handle; 1265 char winfile[MAXPATHLEN]; 1266 void (*init_fct)(); 1267 char *buf; 1268 1269 if (strlen(file) >= MAXPATHLEN) \ rb_loaderror("filename too long"); 1270 1271 /* "Init_xxxx" With the character string which is */ /* said is written to buf, (as for the territory */ /* alloca allotment) */ 1272 init_funcname(&buf, file); 1273 1274 strcpy(winfile, file); 1275 1276 /* Loading the library */ 1277 if ((handle = LoadLibrary(winfile)) == NULL) { 1278 error = dln_strerror(); 1279 goto failed; 1280 } 1281 1282 if ((init_fct = (void(*)()) \ GetProcAddress(handle, buf)) == NULL) { 1283 rb_loaderror("%s - %s\n%s", dln_strerror(), buf, file); 1284 } 1285 1286 /* Init_xxxx()It calls */ 1287 (*init_fct)(); 1288 return handle; 1576 failed: 1577 rb_loaderror("%s - %s", error, file); 1580 } (dln.c)
LoadLibrary ()
doing, GetProcAddress ()
.Because either the fact that it is not,
pattern is the same to here, we will have decided to end