Da nur wenige Menschen den Adventskalender 2016 in C-Sprache schreiben, Ich dachte, ich würde es schreiben und mich daran erinnern, wann ich die C-Sprache berührte.
Letztes Mal habe ich Golang vorgestellt, eine Bibliothek, die mehrere Plattformen unterstützt. Wie oft haben Sie tatsächlich die Möglichkeit, C-Shared-Bibliotheken zu verwenden?
Go unterstützt eine Funktion, die mit einer C / C ++ - Bibliothek namens cgo funktioniert. Sie können die C / C ++ - API problemlos von Go aus aufrufen.
Die Funktion zum Aufrufen von Go aus C / C ++ wird jedoch nur in sehr begrenztem Umfang verwendet. Die Hauptverwendung sind die in Go implementierten Funktionen Ich gehe davon aus, dass es auf Fälle beschränkt ist, in denen Sie es aus einer anderen Sprache verwenden möchten.
Um genauer zu sein, Weil es schwierig ist, die in Go entwickelten Funktionen in andere Sprachen zu portieren Es bedeutet, eine Bibliothek mit c-shared zu erstellen und sie aus einer anderen Sprache aufzurufen. Da c-shared eine C-Schnittstelle ist, kann sie nicht nur in C / C ++, sondern auch verwendet werden. Wie im folgenden Beispiel gezeigt, ist es möglich, über die C-Schnittstelle eine Verknüpfung aus anderen Sprachen herzustellen.
(1) Java -> JNI -> c-shared -> Go (2) JavaScript -> Node(native extension) -> c-shared -> Go (3) ActionScript -> ANE -> c-shared -> Go
Ich suchte bei github nach einem Projekt, das c-shared verwendet, und fand ein schönes.
Suchbegriff site:github.com c-shared buildmode -"golang"
https://github.com/shazow/gohttplib
gohttplib
In einem Projekt, das Go's httpserver von C / python aus aufruft Es scheint das Material zu sein, das auf der PYCON 2016 angekündigt wurde.
Werfen wir einen Blick auf die von c-shared generierte Header-Datei.
libgohttp.h
typedef struct Request_
{
const char *Method;
const char *Host;
const char *URL;
const char *Body;
const char *Headers;
} Request;
typedef unsigned int ResponseWriterPtr;
typedef void FuncPtr(ResponseWriterPtr w, Request *r);
extern void Call_HandleFunc(ResponseWriterPtr w, Request *r, FuncPtr *fn);
...
extern void ListenAndServe(char* p0);
extern void HandleFunc(char* p0, FuncPtr* p1);
extern int ResponseWriter_Write(unsigned int p0, char* p1, int p2);
extern void ResponseWriter_WriteHeader(unsigned int p0, int p1);
Beachten Sie, dass alle C-Shared-Funktionen C-Primitivtypen sind. Oder es ist mit der C-Struktur ausgerichtet.
Es ist ganz anders als Libgo neulich. In libgo könnte Go's ptr so wie es ist aus der C-Welt referenziert werden Ich war wütend auf Cgocheck zur Laufzeit.
libgo.h
struct request_return {
GoSlice r0;
GoInterface r1;
};
extern struct request_return request(GoString p0);
Solange sich alle C-Shared-Funktionen auf der C-Seite befinden, Die gesamte Verarbeitung auf der sendenden Seite ist in Go (cgo) beschrieben.
gohttplib.go
//export HandleFunc
func HandleFunc(cpattern *C.char, cfn *C.FuncPtr) {
// C-friendly wrapping for our http.HandleFunc call.
pattern := C.GoString(cpattern)
http.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
...
// Convert the ResponseWriter interface instance to an opaque C integer
// that we can safely pass along.
wPtr := cpointers.Ref(unsafe.Pointer(&w))
// Call our C function pointer using our C shim.
C.Call_HandleFunc(C.ResponseWriterPtr(wPtr), &creq, cfn)
// release the C memory
C.free(unsafe.Pointer(creq.Method))
C.free(unsafe.Pointer(creq.Host))
C.free(unsafe.Pointer(creq.URL))
C.free(unsafe.Pointer(creq.Body))
C.free(unsafe.Pointer(creq.Headers))
...
responsewriter.go
//export ResponseWriter_WriteHeader
func ResponseWriter_WriteHeader(wPtr C.uint, header C.int) {
w, ok := cpointers.Deref(wPtr)
if !ok {
return
}
(*(*http.ResponseWriter)(w)).WriteHeader(int(header))
}
Interessant ist, dass PtrProxy eine einstufige indirekte Referenz erstellt. ref (), deref (), free () usw. Es ist verantwortlich für die Registrierung, Referenz und Löschung von (Ressourcen-ID, Go ptr).
ptrproxy.go
type ptrProxy struct {
sync.Mutex
count uint
lookup map[uint]unsafe.Pointer
}
// Ref registers the given pointer and returns a corresponding id that can be
// used to retrieve it later.
func (p *ptrProxy) Ref(ptr unsafe.Pointer) C.uint
// Deref takes an id and returns the corresponding pointer if it exists.
func (p *ptrProxy) Deref(id C.uint) (unsafe.Pointer, bool)
// Free releases a registered pointer by its id.
func (p *ptrProxy) Free(id C.uint)
Wenn der durch die Go-Seite gesicherte Zeiger der C-Seite so wie er ist beigebracht wird, Ohne zu wissen, dass Go-Ressourcen von GC verschoben oder von C referenziert werden Es kann von GC gelöscht werden und es kann zu einem unbeabsichtigten Zeitpunkt SEGV sein.
Um diese Möglichkeit auszuschließen, existiert cgocheck und Da es unwahrscheinlich ist, dass die GC unmittelbar nach der Sicherung der Ressourcen erfolgt, libgo funktioniert irgendwie.
Wenn Sie Go from c-shared verwenden, müssen Sie dieses Problem vermeiden und Code schreiben.
Voraussetzung ist, dass die Go-Seite von der C-Seite aufgerufen wird. In einigen Fällen ist es möglich, den Rückruf auf der C-Seite von der Go-Seite aus aufzurufen.
Außerdem ist die Geschichte kompliziert. Wenn Sie sie also im Voraus organisieren, cgo kann in Kommentaren im Go-Quellcode oder in C / C ++ als separater Quellcode geschrieben werden. Schließlich wird es als Go-Bibliothek erstellt.
python
c-shared main or wrapper
+---------+----------------+
| Go, cgo | C/C++ |
+---------+----------------+
Es ist nicht gut, der C-Seite den Zeiger mitzuteilen, dass die Ressource auf der Go-Seite so wie sie ist gesichert ist Methoden wie gohttplib sind gültig. In gohttplib, während der Zeiger verwaltet wird, der durch die Ressource auf der Go-Seite mit Map gesichert ist Dies wird vermieden, indem der C-Seite die ID der Karte mitgeteilt wird. Diese Methode ist so, als würden Sie die Ressourcen auf der Go-Seite als undurchsichtige Zeiger behandeln.
Beim Schreiben des Konvertierungsprozesses von Go-Ressourcen in C-Ressourcen Es ist einfach, mit ptrproxy zu schreiben.
Ressourcenleck, wenn die Karte für immer verarbeitet wird (id, Go ptr) Daher ist es notwendig, Ressourcen zum richtigen Zeitpunkt freizugeben. In gohttplib wird es durch einen expliziten kostenlosen Aufruf aus der Karte gelöscht und ist für GC bestimmt.
Da für die Methode eine Kartenreferenz erforderlich ist, entsteht vor und nach dem Aufruf ein gewisser Overhead. Es ist der Overhead der Grenze zwischen Go und C. Wenn Sie dies also vermeiden möchten, Es ist sicher, eine leistungsfreundliche Implementierung auf einer der Ebenen zu haben.
Es wäre schlecht, dem Zeiger beizubringen, dass die Ressource auf der Go-Seite auf der C-Seite so gesichert ist, wie sie ist. Der im Argument der cgo-Funktion angegebene Zeiger wird vom GC geschützt. Daher ist es effektiv, die Go-Ressource mit dem Argument cgo an die C-Seite zu übergeben.
Kann natürlich als cgo geschrieben werden.
Wenn Sie die als Argumente auf der C-Seite empfangenen Ressourcen auf verschiedene Weise weiterleiten, Es ist notwendig, den Kopiervorgang rechtzeitig auf die C-Seite zu schreiben.
Es ist nicht gut, der C-Seite den Zeiger mitzuteilen, dass die Ressource auf der Go-Seite so wie sie ist gesichert ist Übergeben Sie den auf der C-Seite zugewiesenen Speicher als Argument usw. an die Go-Seite usw. Dies kann vermieden werden, indem der Wert von der Go-Seite in die C-Ressource kopiert wird.
Mit einer Schnittstelle, die Sie über Ressourcen und Rückrufe von der C-Seite informiert Wenn die Go-Seite nur in die Ressource schreibt und den Rückruf aufruft, Diese Methode ist eine einfache Möglichkeit, zusammenzuarbeiten.
Der Wert wird von der Go-Seite in die C-Seite-Ressource kopiert. Weil es notwendig ist, die Ressourcen auf der C-Seite der Go-Seite beizubringen Die Argumente können kompliziert sein. Dies hängt von der ursprünglichen Implementierung auf der C-Seite ab, aber das Ändern der C-Seite kann eine Belastung sein.
Es ist nicht gut, der C-Seite den Zeiger mitzuteilen, dass die Ressource auf der Go-Seite so wie sie ist gesichert ist Rufen Sie die C-Ressourcenzuweisungsfunktion von der Go-Seite aus auf. Sie können dies auch vermeiden, indem Sie dort einen Wert schreiben und dann zur C-Seite zurückkehren (den Eigentümer übertragen). Ist es so, als würde man C.malloc () auf der Go-Seite aufrufen und den Zeiger auf die C-Seite übertragen?
Die Last des Wechsels der C-Seite ist gering.
Der Wert wird von der Go-Seite in die C-Seite-Ressource kopiert. Auf der C-Seite müssen die Ressourcen entsprechend freigegeben werden Die Schnittstelle kann von der C-Seite aus gesehen asymmetrisch sein.
Es gibt eine Möglichkeit, alle Argumente und Ressourcen von c-shared zu vergessen und mit anderen RPCs zu arbeiten. Die einzige Schnittstelle, die c-shared ausgesetzt ist, ist beispielsweise Start / Herunterfahren. Im Folgenden finden Sie eine Methode zum Sprechen mit der REST-API für einen bestimmten Port.
Dies ist eine effektive Methode, wenn die Sprache einfach zu schreiben ist.
Mit der Verwendung von RPC ist ein Overhead verbunden. Möglicherweise müssen Sie auch die RPC-spezifische Fehlerbehandlung berücksichtigen. Hängt das nicht mit der C-Sprache zusammen?
Ich denke unter der Annahme, dass die Go-Seite von der C-Seite aufgerufen wird Wenn PRC im Voraus unterstützt wird (5) Grundsätzlich (2) Wenn Sie eine komplizierte Go-Methode so wie sie ist auf die C-Seite exportieren möchten (1) Wenn Sie Bytes im Rückruf übergeben möchten (3) Es stehen Hilfsmethoden für das create () / free () -System mit komplizierter C-Struktur zur Verfügung. Wenn du es auf der Go-Seite schaffen willst (4)
C-Sprache ist großartig und großartig, weil sie auf verschiedene Arten wiederverwendet werden kann!
Verwenden wir Golang, eine plattformübergreifende Bibliothek!
Ich habe versucht, 3 Optionen als Verwendung von c-shared zu präsentieren, Es gibt eine, die tatsächlich angenommen wird. welcher. Der Manager, der eine solche Implementierungsmethode empfiehlt (ㅍ _ ㅍ)
Recommended Posts