twitter facebook

Ako na moduly v C

programming-CDnes je modularita programov bežná vec. Každý užívateľ určite ocení vysokú konfigurovateľnosť a pluginovatelnosť programu. V tomto príspevku vám ukážem ako napísať modularitu programu.

Ingrediencie: program som písal pod operačným systémom Linux, takže určite gcc, a knižnicu dlfcn ktorá býva bezne obsiahnutá v glibc.

Takže najskôr si povieme ako bude taký modul vyzerať a čo bude obsahovať. Obsahovať bude určite základné informácie o mene autora, názov modulu, stručný popis, verziu modulu a nakoniec licenciu pod ktorou budeme modul publikovať. Ďalej bude obsahovať 2 základné funkcie module_init a module_exit. module_init sa bude volať zakaždým s loadovaním modulu a module_exit zasa pri odstranovaní modulu. Dalšia vec ktorú budeme potrebovať je nejaký loader ktorý bude modul načítavať a funkciu ktorá ho bude mazať. Poďme sa teda pozrieť ako bude vyzerať štruktúra MODULE a čo bude obsahovať:

typedef struct {
	const char	*path;
	void		*handle;
	void		(*init)();
	void		(*exit)();
	struct {
		const char *author;
		const char *title;
		const char *description;
		const char *license;
		const char *version;
	} info;
} MODULE;

Takže premenná path označuje cestu k loadovanému modulu, handle označuje stream z ktorého sa budú načítať premenné a funkcie, dalej tu máme ukazovateľ na funkciu typu void ktorý sa volá init a exit, tie budú ukazovať práve na spomínané funkcie module_init a module_exit. Dalej obsahuje štruktúra MODULE dalšiu štruktúru menom info kde budeme ukladať spomenuté informácie o module.

Ako teda budeme zapisovať informácie o module? Premenné vyzerajúce takto:

const char *__module_info_author = "vl4kn0";

mi neprídu veľmi praktické a navyše sú škaredé. Preto si spravíme pekné makrá aby sme programátorom zjednodušili prácu.
Prvé základné makro bude mať takýto tvar:

#define MODULE_INFO(var, value)	\
 const char __module_info_##var[] = value;

To nám hovorí, že MODULE_INFO(premenna, hodnota) prepíše za const char *__module_info_premenna = hodnota;
To ale stále nieje ono. Preto napíšeme týchto pár makier kôli väčšej komfortnosti:

#define MODULE_AUTHOR(_author)		MODULE_INFO(author, _author)
#define MODULE_LICENSE(_license)	MODULE_INFO(license, _license)
#define MODULE_DESCRIPTION(_desc)	MODULE_INFO(description, _desc)
#define MODULE_TITLE(_title)		MODULE_INFO(title, _title)
#define MODULE_VERSION(_version)	MODULE_INFO(version, _version)

Tieto makrá hovoria o tom, že napríklad MODULE_AUTHOR(”vl4kn0″); prepíše za const char *__module_info_author = “vl4kn0″;

Ďalej tu máme správu chýb. Slúži na vrátenie hodnoty chyby ak takáto chyba naskytne. Môže sa naskytnúť pri alokácii pamäti, otváraní modulu alebo pri načítavaní funkcií z modulu. Kôli tomu som si napísal pár konštánt podla ktorých budem chyby rozpoznávať

#define	EMEM	1  /* Error MEMory allocation */
#define EDLO	2  /* Error Dynamic Library Open */
#define ESYM	3  /* Error getting SYMbol from dynamic library */
#define ENOI	4  /* Error NO Init */
#define	ENOE	5  /* Error NO Exit */

Veľmi jednoduché. Dalej tu budem mať ľ funkcie na prácu s týmyto errorovými číslami a jednu premennú v ktorej bude uložená hodnota posledného erroru.

static unsigned int __module_errno = 0;
 
unsigned int inline
module_errno();
 
void inline
module_raise(unsigned int);

Funkcia module_raise bude ukladať hodnotu do premennej __module_errno a module_errno zase vracia hodnotu uloženú v tejto premennej. Obe obsahujú len jedno priradenie preto sú inline.

A nakoniec tu máme prototypy funkcii module_load a module_unload ktoré modul loadú a unloadujú. Takto teda bude vyzerať prvý súbor module.h

#ifndef __MODULE_H__
#define __MODULE_H__
 
#define	EMEM	1
#define EDLO	2
#define ESYM	3
#define ENOI	4
#define	ENOE	5
 
#define MODULE_INFO(var, value)	\
 const char __module_info_##var[] = value;
 
#define MODULE_AUTHOR(_author)		MODULE_INFO(author, _author)
#define MODULE_LICENSE(_license)	MODULE_INFO(license, _license)
#define MODULE_DESCRIPTION(_desc)	MODULE_INFO(description, _desc)
#define MODULE_TITLE(_title)		MODULE_INFO(title, _title)
#define MODULE_VERSION(_version)	MODULE_INFO(version, _version)
 
typedef struct {
	const char	*path;
	void		*handle;
	void		(*init)();
	void		(*exit)();
	struct {
		const char *author;
		const char *title;
		const char *description;
		const char *license;
		const char *version;
	} info;
} MODULE;
 
static unsigned int __module_errno = 0;
 
unsigned int inline
module_errno();
 
void inline
module_raise(unsigned int);
 
MODULE *
module_load(const char *);
 
void
module_unload(MODULE *);
 
#endif /* __MODULE_H__ */

Teraz si ukážeme ako bude vyzerať súbor module.c. Bude includovat hlavičkové súbory dlfcn.h a module.h a stdlib.h. Dalej tu máme tu nejaké funkcie na prácu s errormi:

void inline
module_raise(unsigned int errno)
{
	__module_errno = errno;
}
 
unsigned int inline
module_errno()
{
	return __module_errno;
}

Tie sú myslím jednoznačné.

Čo nás ale bude viac zaujímať bude funkcia module_load. Za prvé bude vraciať typ MODULE, ktorý som opísal vyššie. Ako parameter bude dostávať cestu k modulu ktorý budeme loadovať. Celkový proces bude vyzerať takto:
vytvorý ukazateľ na premennú typu MODULE, otvorý modul, vybernie z neho informácie, vyberie funkciu init, exit. spustí funkciu init. a tento ukazateľ vráti. Funkcia vyzerá takto:

MODULE *
module_load(const char *path)
{
	MODULE *mod;
 
	mod = (MODULE *)malloc(sizeof(MODULE));
 
	if (!mod) {
		module_raise(-EMEM);
		goto error;
	}
 
	mod->path	= path;
	mod->handle	= dlopen(path, RTLD_LAZY);
	if (!mod->handle) {
		module_raise(-EDLO);
		goto error;
	}
 
	mod->init	= dlsym(mod->handle, "module_init");
	if (!mod->init) {
		module_raise(-ENOI);
		goto error;
	}
 
	mod->exit	= dlsym(mod->handle, "module_exit");
	if (!mod->exit) {
		module_raise(-ENOE);
		goto error;
	}
 
	mod->info.author	= dlsym(mod->handle, "__module_info_author");
	mod->info.title		= dlsym(mod->handle, "__module_info_title");
	mod->info.description	= dlsym(mod->handle, "__module_info_description");
	mod->info.license	= dlsym(mod->handle, "__module_info_license");
	mod->info.version	= dlsym(mod->handle, "__module_info_version");
 
	mod->init();
 
	return mod;
error:
	return NULL;
}

Všimite si, že vždy keď môže nastať chybová situácia máme oštrenie pomocou funkcie module_raise.
Dalšia a posledná funkcia v tomto súbore bude funkcia module_unload. Ako parameter preberá ukazateľ na modul ktorý chceme unloadovať. Postup: zistí či je predávaný modul alokovaný, ak nieje funkcia skončí. Zavolá module_exit, uzavrie modul, a informácie odstráni z pamäti, kód je tu:

void
module_unload(MODULE *mod)
{
	if (!mod)
		return;
 
	mod->exit();
 
	if (mod->handle)
		dlclose(mod->handle);
 
	free(mod);
}

na zrekapitulovanie celý kod:

#include <stdlib.h>
#include <dlfcn.h>
 
#include "module.h"
 
void inline
module_raise(unsigned int errno)
{
	__module_errno = errno;
}
 
unsigned int inline
module_errno()
{
	return __module_errno;
}
 
MODULE *
module_load(const char *path)
{
	MODULE *mod;
 
	mod = (MODULE *)malloc(sizeof(MODULE));
 
	if (!mod) {
		module_raise(-EMEM);
		goto error;
	}
 
	mod->path	= path;
	mod->handle	= dlopen(path, RTLD_LAZY);
	if (!mod->handle) {
		module_raise(-EDLO);
		goto error;
	}
 
	mod->init	= dlsym(mod->handle, "module_init");
	if (!mod->init) {
		module_raise(-ENOI);
		goto error;
	}
 
	mod->exit	= dlsym(mod->handle, "module_exit");
	if (!mod->exit) {
		module_raise(-ENOE);
		goto error;
	}
 
	mod->info.author	= dlsym(mod->handle, "__module_info_author");
	mod->info.title		= dlsym(mod->handle, "__module_info_title");
	mod->info.description	= dlsym(mod->handle, "__module_info_description");
	mod->info.license	= dlsym(mod->handle, "__module_info_license");
	mod->info.version	= dlsym(mod->handle, "__module_info_version");
 
	mod->init();
 
	return mod;
error:
	return NULL;
}
 
void
module_unload(MODULE *mod)
{
	if (!mod)
		return;
 
	mod->exit();
 
	if (mod->handle)
		dlclose(mod->handle);
 
	free(mod);
}

Takže základ máme zmáknutý stačí napísať example modul, ktorý bude vyzerať veľmi jednoducho:

#include <stdio.h>
#include "module.h"
 
MODULE_AUTHOR("vl4kn0");
MODULE_TITLE("example");
MODULE_DESCRIPTION("example for byteleak.com");
 
void module_init()
{
	puts("load");
}
 
void module_exit()
{
	puts("unload");
}

Veľmi jednoduché. Tento príklad teraz skompilujeme, ako shared library. To docielime prepínačon -shared

gcc -shared -o example.mo example.c

koncovka mo znamená module object. Môžete si samozrejme zvolit akú budete chcieť.
Teraz máme náš prvý modul. Podme sa teda pozrieť ako sa naloaduje.
Napíšeme teda jednoduchý program, ktorý naloaduje modul a vypíše autorove meno.

#include <stdio.h>
#include "module.h"
 
int main(int argc, char **argv)
{
	MODULE *mod;
 
	mod = module_load("./example.mo");
	printf("%s\n", mod->info.author);
	module_unload(mod);
 
	return 0;
}

Tento program pomenujeme main.c. Skompilujeme ho teda spolu s module.c a knižnicou dlfcn

gcc main.c module.c -ldl -o main

Po spustení dostaneme takýto výstup:

load
vl4kn0
unload

Program vypísal load tak ako to bolo definované vo funkcii module_init, potom vypísal autorove meno
tak ako je to napísané vo funkcii main a nakoniec sa unloadoval a spustil module_exit co malo za následok vypísanie unload.

Dúfam, že sa vám článok páčil. Ak áno alebo aj ak nie píšte komenty.




Žiadne odpovede v “Ako na moduly v C”

Článok “Ako na moduly v C” zatiaľ ešte nikto nekomentoval. Buď prvý, kto vyjadri svoj názor.
 
 
 

* Email nebude nikde uverejnený.


Pravidlá: Tie su jednoduché, vyhni sa invektívam a spamovaniu, a môžeš si byť istý, že tvoj komentár tu zostane. Tu si tykáme.

Môžeš použiť tieto tagy: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">


Komentáre

Mark Zuckerberg chce spoplatniť Facebook

schusterr - 31.07.2010 04:17
ten Mark Zuckerberger je miliardar , ten uz moc lowe k zivotu nepotrebuje :)

Ako na zvonenie pre iPhone?

ByteLeak - 16.07.2010 10:39
Jednoducho si najdes priecinok zo zvoneniami (pravy klik na ten upraveny song, a Show in windows Explorer) ...

Ako na zvonenie pre iPhone?

andrea - 16.07.2010 08:16
nie som velmi technicky typ teda, ale zda sa mi to celkom logicke :) ale tak ci tak som natrafila na ...

Mark Zuckerberg chce spoplatniť Facebook

Baribylina - 04.07.2010 11:41
Či už je to pravda alebo nie..existuje mnoho podobných(i lepších)blogov, na ktore sa da bez problemov ...

Ako na zvonenie pre iPhone?

Lukáš - 11.06.2010 14:49
bohužial nemam tam takú možnosť create AAC version..
ByteLeak.com
Hore
35 queries in 0.454 seconds.