Very late in the pre-2.4.0 development series, the kernel developers added a new interface providing limited communication between modules. This intermodule scheme allows modules to register strings pointing to data of interest, which can be retrieved by other modules. We’ll look briefly at this interface, using a variation of our master and slave modules.
We use the same master module, but
introduce a new slave module called inter.
All inter does is to make a string and a
function available under the name ime_string
(ime
means “intermodule example”) and
ime_function
; it looks, in its entirety, as
follows:
static char *string = "inter says 'Hello World'"; void ime_function(const char *who) { printk(KERN_INFO "inter: ime_function called by %s ", who); } int ime_init(void) { inter_module_register("ime_string", THIS_MODULE, string); inter_module_register("ime_function", THIS_MODULE, ime_function); return 0; } void ime_cleanup(void) { inter_module_unregister("ime_string"); inter_module_unregister("ime_function"); }
This code uses inter_module_register, which has this prototype:
void inter_module_register(const char *string, struct module *module, const void *data);
string
is the string other modules will use to find
the data; module
is a pointer to the module owning
the data, which will almost always be THIS_MODULE
;
and data
is a pointer to whatever data is to be
shared. Note the use of a const
pointer for the
data; it is assumed that it will be exported in a read-only mode.
inter_module_register will complain (via
printk) if the given string
is
already registered.
When the data is no longer to be shared, the module should call inter_module_unregister to clean it up:
void inter_module_unregister(const char *string);
Two functions are exported that can access data shared via inter_module_register:
const void *inter_module_get(const char *string);
This function looks up the given string
and returns
the associated data pointer. If the string has not been registered,
NULL
is returned.
const void *inter_module_get_request(const char *string, const char *module);
This function is like inter_module_get with the
added feature that, if the given string
is not
found, it will call request_module with the given
module
name and then will try again.
Both functions also increment the usage count for the module that registered the data. Thus, a pointer obtained with inter_module_get or inter_module_get_request will remain valid until it is explicitly released. At least, the module that created that pointer will not be unloaded during that time; it is still possible for the module itself to do something that will invalidate the pointer.
When you are done with the pointer, you must release it so that the other module’s usage count will be decremented properly. A simple call to
void inter_module_put(const char *string);
will release the pointer, which should not be used after this call.
In our sample master module, we call inter_module_get_request to cause the inter module to be loaded and to obtain the two pointers. The string is simply printed, and the function pointer is used to make a call from master into inter. The additional code in master looks like this:
static const char *ime_string = NULL; static void master_test_inter(); void master_test_inter() { void (*ime_func)(); ime_string = inter_module_get_request("ime_string", "inter"); if (ime_string) printk(KERN_INFO "master: got ime_string '%s' ", ime_string); else printk(KERN_INFO "master: inter_module_get failed"); ime_func = inter_module_get("ime_function"); if (ime_func) { (*ime_func)("master"); inter_module_put("ime_function"); } } void master_cleanup_module(void) { if (ime_string) inter_module_put("ime_string"); }
Note that one of the calls to inter_module_put is deferred until module cleanup time. This will cause the usage count of inter to be (at least) 1 until master is unloaded.
There are a few other worthwhile details to keep in mind when using
the intermodule functions. First, they are available even in kernels
that have been configured without support for loadable modules, so
there is no need for a bunch of #ifdef
lines to
test for that case. The namespace implemented by the intermodule
communication functions is global, so names should be chosen with care
or conflicts will result. Finally, intermodule data is stored in a
simple linked list; performance will suffer if large numbers of
lookups are made or many strings are stored. This facility is
intended for light use, not as a general dictionary subsystem.