Artykuł jest wprowadzeniem do pisania modułów NLM dla osób
początkujących, które nie
wiedzą jak zacząć.
Do tworzenia modułów wykonywalnych na serwerze (NLM)
potrzebne jest oprogramowanie
CodeWarrior firmy Metrowerks
lub kompilator firmy Watcom.
W tym artykule opisane zostało użycie kompilatora Watcom, obecnie dostępnego
na
zasadach Open Source. Niezbędne minimalne oprogramowanie jest dostępne tutaj.
1.0 Przygotowanie środowiska do pracy.
W minimalnej wersji, do tworzenia programów wykonywalnych na
serwerze NetWare potrzebny
jest system DOS. Zakładając, że całość oprogramowania zainstalujemy na dysku C
należy
dopisać do zbioru AUTOEXEC.BAT następujące linie:
PATH C:\WATCOM\BINW
SET INCLUDE=C:\WATCOM\H
SET WATCOM=C:\WATCOM
Pobrane oprogramowanie kompilatora Watcom należy wgrać na dysk C z zachowaniem
zarchiwizowanej struktury plików.
W załączonym pliku znajduje się "okrojony" (ze względu na jego
rzeczywistą objętość
kiludziesięciu MB) katalog NWSDK. Do właściwej pracy należy pobrać aktualną
wersję
biblioteki NDK z http://developer.novell.com/ndk/nwsdkc.htm
2.0 Przykład wzorcowego modułu.
Zbiór źródłowy modułu NLM8.C znajduje się w katalogu
WATCOM\NLM8.
Poniżej przedstawiony został wzorzec wielowątkowego modułu NLM. Oprócz głównego
wątka
programu otwierane są dwa dodatkowe.
//==========================================
// WZÓR WIELOWĄTKOWEGO MODUŁU NLM
//==========================================
//Includes
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <nwconio.h>
#include <nwthread.h>
#include <nwenvrn.h>
#include <nwerrno.h>
#include <ntypes.h>
//Globals
int NLM_MainThreadGroupID;
//This is the thread group ID for the main() thread
int NLM_ThreadCount = 0;
//Keeps count of number of threads running
int NLM_Exiting = FALSE;
//Flag that threads poll to know when to exit
int NLM_Thread1_ID;
//Thread ID for second thread we created in main()
int NLM_Thread2_ID;
//Thread ID for third thread we created in main()
int Thread1_ScreenHandle = NULL;
//Handle for screen created in thread 1
//Function prototypes
void main(void);
void NLM_SignalHandler(int sig);
void Thread1Func(void *);
void Thread2Func(void *);
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// *********** MAIN ********
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void main(void)
{
//Increment thread count
++NLM_ThreadCount;
//Get this thread's ID from OS
NLM_MainThreadGroupID = GetThreadGroupID();
//Register NLM_SignalHandler function for SIGTERM [UNLOAD] event
signal(SIGTERM, NLM_SignalHandler);
//Register NLM_SignalHandler function for SIGINT [CTRL-C] event
signal(SIGINT, NLM_SignalHandler);
ConsolePrintf("\nNLM8: Thread 0 (main() thread) started
and running!\n\r");
//////////////////////////////////
// Body of main continues here...
//////////////////////////////////
//Let's start up a new thread
NLM_Thread1_ID = BeginThread(Thread1Func, NULL, 8192, NULL);
if(NLM_Thread1_ID == EFAILURE)
{
ConsolePrintf("\n\rNLM8: ERROR: Could not start thread
1. BeginThread returned: %X\n\r", NLM_Thread1_ID);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//Now let's start up another thread
NLM_Thread2_ID = BeginThread(Thread2Func, NULL, 8192, NULL);
if(NLM_Thread2_ID == EFAILURE)
{
ConsolePrintf("\n\rNLM8: ERROR: Could not start thread
2. BeginThread returned: %X\n\r", NLM_Thread2_ID);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//////////////////////////////////
// Body of main ends here...
//////////////////////////////////
//Make sure the main() thread is the last to exit!
while(NLM_ThreadCount != 1 || !NLM_Exiting)
ThreadSwitchWithDelay();
ConsolePrintf("NLM8: main() thread exiting!\n\r");
//Bring thread count to zero so that the signal
//handler can now exit, if necessary
--NLM_ThreadCount;
return;
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// Signal Handler
// This callback function will be called from the
// Server Console OS thread when the NLM is
// manually UNLOADed from the console,
// or if the server is going down,
// or if SIGTERM is raise()'ed for this NLM.
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void NLM_SignalHandler(int sig)
{
int handlerThreadGroupID;
handlerThreadGroupID = GetThreadGroupID();
SetThreadGroupID(NLM_MainThreadGroupID);
switch(sig)
{
//SIGTERM - NLM is being UNLOADed,
//or otherwise terminated externally [e.g. Server going down]
case SIGTERM:
//Set flag to tell all threads it's time to leave
NLM_Exiting = TRUE;
////////////////////////////////////////
//NLM SDK functions may be called here//
//Don't do your cleanup here. //
//Do this in each thread instead. //
////////////////////////////////////////
//Wait until all threads have terminated before leaving
while(NLM_ThreadCount)
{
//This print statement is just for illustration.
ConsolePrintf("NLM8: SignalHandler:
Waiting for threads to terminate...\n\r");
ThreadSwitchWithDelay();
}
ConsolePrintf("NLM8: SignalHandler: Done!\n\r");
break;
default:
break;
}
SetThreadGroupID(handlerThreadGroupID);
return;
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// This is the entry point for the second thread (created in main())
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void Thread1Func(void *arg)
{
arg = arg; //suppress warnings
//Increment thread count
++NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 1 started and running!\n\r");
//Create a screen for this thread for use by this NLM.
//Note that this automatically becomes the current [active] screen for this
//thread, because there are no other screens in this NLM.
Thread1_ScreenHandle = CreateScreen("NLM TEMPLATE
Multi-thread Screen", AUTO_DESTROY_SCREEN);
if(Thread1_ScreenHandle == EFAILURE || Thread1_ScreenHandle == NULL)
{
ConsolePrintf("\n\rNLM8: ERROR: CreateScreen returned: %X\n\r",
Thread1_ScreenHandle);
//Don't exit immediately, set NLM_Exiting flag instead
NLM_Exiting = TRUE;
}
//Thread1 run loop
while(!NLM_Exiting)
{
printf("Thread 1 is printing this text!\n");
//Press a key on this screen to terminate the NLM
if(kbhit())
{
NLM_Exiting = TRUE;
printf("Time to leave!!\n");
}
delay(500); //put this thread to sleep for 500ms.
}
//Clean up this thread's resources
if(Thread1_ScreenHandle != NULL)
DestroyScreen(Thread1_ScreenHandle);
//Reset to NULL so other threads won't try to use
// it now that it has been destroyed
Thread1_ScreenHandle = NULL;
//Decrement thread count
--NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 1 shutdown...\n\r");
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
// This is the entry point for the third thread (created in main())
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
void Thread2Func(void *arg)
{
arg = arg; //suppress warnings
//Increment thread count
++NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 2 started and running!\n\r");
//Set this thread's active screen to the screen we created in thread 1
if(Thread1_ScreenHandle != NULL)
SetCurrentScreen(Thread1_ScreenHandle);
else
NLM_Exiting = TRUE;
//Thread2 run loop
while(!NLM_Exiting)
{
if(Thread1_ScreenHandle != NULL) //make sure the screen is still valid
printf("This text comes from Thread 2!\n");
delay(500); //put this thread to sleep for 500ms.
}
//Decrement thread count
--NLM_ThreadCount;
ConsolePrintf("NLM8: Thread 2 shutdown...\n\r");
}
//°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
3.0 Kompilacja i linkowanie.
Kompilację modułu można wykonać wywołując zbiór komp.bat
zawierający polecenie:
set pgm=nlm8
wcc386 -D2 -ms -w4 -5s -bt=NetWare -Ic:\nwsdk nlm8.c
Kompilację i linkowanie modułu można wykonać wywołując zbiór link.bat
zawierający polecenia:
set pgm=nlm8
wcc386 -D2 -mf -w4 -5s -5r -bm -fp5 -fpi87 -eq -bt=NetWare -Ic:\nwsdk nlm8.c
wlink @nlm8.lnk
Parametry linkowania zapisane są w zbiorze NLM8.LNK:
SYSTEM NOVELL 'WZORZEC NLM'
debug all
file nlm8
name nlm8.nlm
option map
option stack=16384
option copyright 'Copyright by DJack Software 04/2003. All rights reserved.'
option version = 1.1
import @c:\nwsdk\imports\nwsnut.imp
import @c:\nwsdk\imports\clib.imp
import @c:\nwsdk\imports\dsapi.imp
import @c:\nwsdk\imports\calnlm32.imp
import @c:\nwsdk\imports\clxnlm32.imp
import @c:\nwsdk\imports\netnlm32.imp
module nwsnut.nlm
module clib.nlm
module calnlm32.nlm
module clxnlm32.nlm
module netnlm32.nlm
W wyniku otrzymamy moduł NLM8.NLM, który możemy uruchomić na
serwerze NetWare.
Więcej informacji dostępne jest w dokumentacji kompilatora WATCOM.
|