Seiten

Warum diese Seite entstanden ist:
Unter andrem Programmiere ich auch in Java und ich habe ein Chipdrive mit dem man Speicherkarten und andre Karten mit einem Chip drauf einlesen kann. Nun lag die Idee nahe, das Chipdrive mal Prorammtechnisch zu nutzen. Die Firma Towitoko (mittlerweile aufgekauft worden von SCM) hatte auch eine Schnittstelle für Linux mit geliefert und mein Ziel war es, diese CT-API Schnittstelle die in C programmiert ist, in JAVA ein zubinden.
Da diese CT-API Schnittstelle als shared Library im System verankert ist kam diese Seite bei raus.
Zuerst habe ich mich also mit dem JNI Interface in Java befasst und wie fast immer wenn ich irgendwo einsteige hatte ich mit ettlichen Fallstricken zu kämpfen - Die Fallstricke möchte ich den geneigten Surfer vermeiden helfen :-)
Denn wie ich festellte, waren die Beschreibungen von Sun für mein System zwar hilfreich aber fehlerhaft.
Genauso waren die Beschreibung von shared Librarys die ich gefunden habe - aus diesen Grunde :
Wie erstelle ich für C eine Shared Library
Wie erstelle ich eine Shared Library die eine andere Library benutzt
Wie erstelle ich eine JNI Library


Librarys

Eine Kurze Anleitung wie man shared Librarys anlegt und benutzt
Mein System ist ein Suse 8.0 Linux System.

Die Testdateien:

unterlib.c

#include <stdio.h>

void unterhello(void) {
printf("hallo hier ist die unterlibrary\n");
}

unterlib.h

void unterhello(void);

demo.c

#include <stdio.h>
#include "unterlib.h"

int main(void) {
unterhello();
printf("mainprogramm\n");
return 0;
}

gcc -fPIC -Wall -g -c unterlib.c
erzeugt das file unterlib.o

gcc -g -shared -Wl,-soname,libunterlib.so.0 -o libunterlib.so.0.0 unterlib.o -lc
erzeugt das Library
die namensgebung ist hier wichtig - Librarys fangen mit "lib" an dann kommt der Name punkt "so" punkt versionsnummer punkt releasenummer
libunterlib.so.0.0 ist der Realname, die eigendliche Library

als root die datei /etc/ld.so.conf ansehen, dort sind die Verzeichnisse aufgelistet in die Librarys gespeichetr werden können.
z.B. /usr/local/lib hier das Libraryfile hinkopieren dann mit
ln -sf libunterlib.so.0.0 libunterlib.so.0
ln -sf libunterlib.so.0 libunterlib.so
zwei Links setzen
mit nm libunterlib.so kann man die Library kontrollieren.

ldconfig starten um dem System die Library bekannt zu machen
In einigen Beschreibungen ist von der Variable LD_LIBRARY_PATH die rede - bei meinen test mit den Librarys wird die Variable nicht ausgewertet :-(
auch macht es keinen Sinn das Programm ldconfig (auch mit den in den Option -n .) wie es in den Beschreibungen gezeigt wird, als User auszuführen!

so weiter als user:
Hauptprogramm übersetzen:
gcc -Wall -g -c demo.c
gcc -g -o demo demo.o -lunterlib
mit der Option -l wird beim Übersetzen dem Programm mitgeteilt, das es die Library unterlib benutzen soll - hier wird auch klar warum die Namengebung beim erstellen der Library so wichtig ist denn -l setzt vorraus das der Name mit lib anfängt und mit .so endet
-l unterlib macht also eigendlich Namen libunterlib.so dem Programm bekannt !
Noch eine Anmerkung - die Reihenfolge der Optionen von gcc ist nicht egal! erst kommt die Programmquelle dann mit l die anbindungen zu bentigten Librarys - eine andre Reihenfolge macht zwar keine Übersetzungsfehler, aber das erstellte Programm wird nicht gehen.

So das war zum Aufwärmen :-)
Hier kommt eine Library die eine andre Library benutzt.

testsub.c

#include "unterlib.h"
#include <stdio.h>

void libhello(void) {
printf("start unterhallo \n");
unterhello();
printf("normal lib ende\n");
}

die testsub.h

void libhello(void);

die demo2.c

#include <stdio.h>
#include "testsub.h"

int main(void) {
libhello();
printf("mainprogramm 2\n");
return 0;
}


recht einfach gelle -
gcc -fPIC -Wall -g -c testsub.c
gcc -g- shared -Wl,-soname,libtestsub.so.0 -o libtestsub.so.0.0 testsub.o -lunterlib -lc

wie auch bei der erstellung der normalem Library nur der zurätzliche link kommt noch dazu
als root:
wieder copieren ,links setzen, ldconfig starten
und dann als normaler User das Hauptprogramm übersetzen, alles so wie schon gezeigt

Hier kommt zum ersten mal Java in Spiel

Meine Anleitung betrifft den Jbuilder von Borland.

die Java Quelldatei

class Jnimain {

static int zahl;

public static native int jniint(int inp);

public static void main(String argv[]) {
 System.out.println("Start");
 System.out.println(System.getProperty("java.library.path"));
 zahl = jniint(5);
 System.out.println(zahl);
}

static {
System.load("/home/wolfgang/jbproject/jniprojekt/classes/Jnimain.so");
//System.loadLibrary("testjni");
}

soweit so gut - in der ersten Version müssen die farblich gekennzeichneten Zeilen als Kommentar ausgeschaltet werden.
Man lässt die Datei ausführen damit man die Jnimain.class Datei bekommt.
Bei Jbuilder sind im Verzeichnis jbprojekt alle Projekte mit eigenen Unterverzeichnis vertreten. In meinem Fall was es das Projekt jniprojekt also im Verzeichnis jbprojekt/jniprojekt/classes landet die Jnimain.class.
In das Verzeichnis wechseln und mit javah Jnimain die Jnimain.h Dateierzeugen lassen. So was kommt dann in meinem Beispiel heraus:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni .h>
/* Header for class Jnimain */
#ifndef _Included_Jnimain
#define _Included_Jnimain
#ifdef __cplusplus
extern "C" {
#endif
/* Inaccessible static: zahl */
/*
 * Class: Jnimain
 * Method: jniint
 * Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_Jnimain_jniint (JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif


als erstes wird diese Dateikopiert als Jinimain.c , denn eine Erkenntnis die ich ermittelte ist:
die Jnimain.h Datei wird nicht mehr gebraucht - wenn man sie wie bei einigen anderen Seiten Vorschlägt mit include in der c Datei einfügt, wird das Library nachher ein Linkfehler erzeugen :-(
Also trotz Warnung wird die Datei als c Datei editiert und sieht dann so aus:

#include <jni.h>
/* Header for class Jnimain */
#ifndef _Included_Jnimain
#define _Included_Jnimain
#ifdef __cplusplus
extern "C" {
#endif
/* Inaccessible static: zahl */
/*
 * Class: Jnimain
 * Method: jniint
 * Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_Jnimain_jniint (JNIEnv *env, jclass obj, jint inp) {
 jint ex = inp + inp;
 return ex;
}
#ifdef __cplusplus }
#endif
#endif

Beachte die Namensergänzung für die Variablen im Aufruf der Function
jetzt kommt die Erstellung des nötigen library.so Datei
ein kleines shellscript macht für und die arbeit:
#!/bin/sh
# Verzeichnis von Java
vzjava="/home/susanne/JBuilder8/jdk1.4"
gcc -Wall -fPIC -I$vzjava/include -I$vzjava/include/linux -c $1.c 
gcc -shared -Wl,-soname,$1.so -o $1.so $1.o

Aufruf wenn das shellscript dolib benannt wird ist:
dolib Jinimain
der erste Aufruf im shellscript erzeugt ein Jnimain.o der zweite erzeugt das Library.so File das Verzeichnis muss natürlich den Gegebenheiten angepasst werden :-)
Wir man sehen kann nutze ich System.load (hier muss ein absoluter Pfad und der komplette Name der Library Files also mit der so endung angeben werden )
aber das war das einzige was ich zum laufen bekommen habe. Die Version mit System.loadLibrary ist bei mir immer gescheitert obwohl ich versucht habe die erzeugte Library in den von dem Befehl
System.out.println(System.getProperty("java.library.path"));
angegebenen Verzeichnisse kopiert hatte - nie wurde es gefunden. Aber meine Version geht wenigstens :-)