2013-06-10 15 views
7

Pracuję z kilkoma modułami jądra Linux i mam pytanie związane z problemem z okrągłym ładowaniem.Czy ładowanie eksportowanych symboli może być opóźnione?

Moduł A ładuje pierwszy i eksportuje wiele symboli do użycia w module B lub C. Następnie moduł B lub C zostaje załadowany, a symbole istnieją dla ich użycia.

Jednak obecnie stwierdzam, że moduł A wymaga symbolu z modułu B lub C, ale tylko w czasie uruchamiania i nie jest potrzebny do zainicjowania modułu. Więc oczywiście, kiedy A się ładuje, okazuje się, że symbol jeszcze nie istnieje. W module A miałem nawet symbol oznaczony jako extern, ale to też nie działa.

Czy można opóźnić ładowanie symbolu po załadowaniu modułu A, ale jeszcze nie istnieje, dopóki nie załadowano B lub C?

Odpowiedz

5

Takie sytuacje są często rozwiązywane za pomocą wywołań zwrotnych.

Załóżmy, że moduł A eksportuje funkcje rejestrowania/wyrejestrowywania wywołań zwrotnych. B i/lub C używają tych funkcji i przekazują odpowiednie wywołania zwrotne do A. Gdy jest to potrzebne, A sprawdza, czy wywołania zwrotne są ustawione i wywołuje je.

Coś takiego (bez obsługi błędów i blokowania dla uproszczenia):

/* Module A */ 
struct a_ops /* Better to define struct a_ops in a header file */ 
{ 
    void (*needed_func)(void); 
    void (*another_needed_func)(void); 
}; 
... 
struct a_ops ops = { 
    .needed_func = NULL; 
    .another_needed_func = NULL; 
}; 
... 
int a_register_needed_funcs(struct a_ops *a_ops) 
{ 
    ops.needed_func = a_ops->needed_func; 
    ops.another_needed_func = a_ops->another_needed_func; 
} 
EXPORT_SYMBOL(a_register_needed_funcs); 

void a_unregister_needed_funcs() 
{ 
    ops.needed_func = NULL; 
    ops.another_needed_func = NULL; 
} 
EXPORT_SYMBOL(a_unregister_needed_funcs); 

... 
/* Call the specified callbacks when needed: */ 
void do_something(void) 
{ 
    if (ops.needed_func != NULL) { 
     ops.needed_func(); 
    } 
    else { 
      /* the callback is not set, handle this: report error, ignore it or 
      * do something else */ 
      ... 
    } 
} 
... 

/* Modules B and C */ 
/* Their code #includes the file where struct a_ops is defined. 
* The module registers the callbacks, for example, in its init function 
* and unregister in exit function. */ 
... 
static void func(void) 
{ 
    ... 
} 

static void another_func(void) 
{ 
    ... 
} 

struct a_ops my_funcs = { 
    .needed_func = func; 
    .another_needed_func = another_func; 
}; 

int __init my_module_init(void) 
{ 
    ... 
    result = a_register_needed_funcs(&my_funcs); 
    ... 
} 
void __exit my_module_exit(void) 
{ 
    ... 
    a_unregister_needed_funcs(); 
    ... 
} 

ten jest podobny do pliku operacji i wiele innych operacji zwrotnych w jądrze. Załóżmy, że użytkownik chce odczytać, powiedzmy, urządzenie znakowe obsługiwane przez niestandardowy sterownik. Właściwe jądro (dokładniej VFS) odbiera żądanie, ale samo nie może go obsłużyć. Przekazuje żądanie do tego niestandardowego sterownika, który zarejestrował wywołania zwrotne operacji na tym urządzeniu. Z kolei sterownik używa funkcji eksportowanych przez właściwe jądro, np. cdev_add(), itp.

1

Jeśli znasz typ/prototyp symbolu, spróbuj użyć kallsyms_lookup_name(), aby uzyskać wskaźnik do pożądanego symbolu w czasie pracy zamiast do niego jako zewnętrznego symbolu (co oznacza, że ​​program ładujący sprawdzi go przy ładowaniu czas). Przykłady można znaleźć za pomocą ulubionej wyszukiwarki.