2012-05-07 13 views
10

Disclaimer: l'autore della domanda ha una conoscenza media di Erlang e una conoscenza di base di C.C e Erlang: esempio Erlang Port

Sto leggendo il Interoperability Tutorial User Guide ora. Ho compilato con successo l'esempio complex.c e funziona con la porta Erlang senza problemi.

Tuttavia, mi piacerebbe capire come funziona il codice C attuale. Lo capisco in generale: nell'esempio legge 2 byte dallo standard input e controlla il primo byte. A seconda del primo byte, chiama la funzione foo o bar. Questo è il limite della mia comprensione di questo momento.

Quindi, se prendiamo entrambi erl_comm.c:

/* erl_comm.c */ 

typedef unsigned char byte; 

read_cmd(byte *buf) 
{ 
    int len; 

    if (read_exact(buf, 2) != 2) 
    return(-1); 
    len = (buf[0] << 8) | buf[1]; 
    return read_exact(buf, len); 
} 

write_cmd(byte *buf, int len) 
{ 
    byte li; 

    li = (len >> 8) & 0xff; 
    write_exact(&li, 1); 

    li = len & 0xff; 
    write_exact(&li, 1); 

    return write_exact(buf, len); 
} 

read_exact(byte *buf, int len) 
{ 
    int i, got=0; 

    do { 
    if ((i = read(0, buf+got, len-got)) <= 0) 
     return(i); 
    got += i; 
    } while (got<len); 

    return(len); 
} 

write_exact(byte *buf, int len) 
{ 
    int i, wrote = 0; 

    do { 
    if ((i = write(1, buf+wrote, len-wrote)) <= 0) 
     return (i); 
    wrote += i; 
    } while (wrote<len); 

    return (len); 
} 

e port.c:

/* port.c */ 

typedef unsigned char byte; 

int main() { 
    int fn, arg, res; 
    byte buf[100]; 

    while (read_cmd(buf) > 0) { 
    fn = buf[0]; 
    arg = buf[1]; 

    if (fn == 1) { 
     res = foo(arg); 
    } else if (fn == 2) { 
     res = bar(arg); 
    } 

    buf[0] = res; 
    write_cmd(buf, 1); 
    } 
} 

Che cosa significa ogni funzione effettivamente fare lì? Che scopo servono effettivamente le variabili li, len, i, wrote, got?

Alcune più piccole domande:

  1. Perché le funzioni non hanno alcun tipo di ritorno, anche void s?
  2. Quando la porta di Erlang invia i dati a C, il primo byte determina una funzione da chiamare. Se il byte contiene il decimale 1, viene chiamato foo(), se il byte contiene il decimale 2, quindi viene chiamato bar(). In caso contrario, è possibile utilizzare questo protocollo per richiamare fino a 255 funzioni C diverse con un solo parametro ciascuna. È giusto?
  3. "L'aggiunta dell'indicatore di lunghezza verrà eseguita automaticamente dalla porta di Erlang, ma deve essere eseguita esplicitamente nel programma C esterno". Cosa significa? Su quale linea di codice è fatta?
  4. Dall'esercitazione: "Per impostazione predefinita, il programma C deve leggere dallo standard input (descrittore di file 0) e scrivere sullo standard output (descrittore di file 1)." Quindi: "Nota che stdin e stdout sono per input/output bufferizzati e non dovrebbero essere usati per la comunicazione con Erlang!" Qual è il trucco qui?
  5. perché buf viene inizializzato su [100]?

risposta

8

Questa risposta è pure declinata (io non sono un Erlang o un programmatore C, Ho appena capita di essere passare attraverso lo stesso materiale)

Il modello iniziale è un po 'fuori. Il modo in cui il codice funziona è leggendo i primi due byte da stdin, assumendo che significhi la lunghezza del messaggio effettivo, quindi leggendo molti più byte da stdin. In questo caso specifico, accade che il messaggio attuale sia sempre di due byte (un numero corrispondente a una funzione e un singolo argomento intero da passare ad esso).

0 - a)read_exact legge len byte da stdin, read_cmd utilizza read_exact prima per stabilire quanti byte si deve leggere (o un numero significate dalle prime due byte, o nessuno se ci sono meno di due byte disponibili), e quindi per leggere quel numero di byte. write_exact scrive len byte a stdout, write_cmd utilizza write_exact per generare un'intestazione di lunghezza di due byte, seguita da un messaggio (si spera) della lunghezza appropriata.

0 - b) Penso che len sia sufficientemente coperto sopra. li è il nome della variabile utilizzata per generare l'intestazione a due byte per la funzione di scrittura (non è possibile eseguire le operazioni di spostamento dei bit passo per passo, ma il risultato finale è che len è rappresentato nei primi due byte inviati). i è una variabile intermedia il cui scopo principale sembra essere quello di assicurarsi che write e read non restituiscano un errore (se lo fanno, tale codice di errore viene restituito come risultato di read_exact/write_exact). wrote e got tiene traccia di quanti byte sono stati scritti/letti, i cicli di contenimento escono prima che diventi maggiore di len.

1 - In realtà non sono sicuro. Le versioni con cui stavo lavorando sono di tipo int, ma per il resto identiche. Ho ottenuto il mio dal capitolo 12 di Programming Erlang anziché dalla guida che colleghi.

2 - Questo è corretto, ma il punto del protocollo della porta è che si può cambiare per inviare diversi argomenti (se si sta inviando arbitrarie argomenti, sarebbe probabilmente una migliore idea di utilizzare solo il Metodo C Node anziché porte). Ad esempio, I modified it subtly in un pezzo recente in modo che invii una stringa singola, poiché ho solo una funzione che desidero chiamare sul lato C, eliminando la necessità di specificare una funzione. Dovrei anche menzionare che se hai un sistema che ha bisogno di chiamare più di 255 diverse operazioni scritte in C, potresti voler ripensare alla sua struttura (o semplicemente andare all'intero nove e scrivere tutto in C).

3 - Questo viene fatto

read_cmd(byte *buf) 
{ 
    int len; 

    if (read_exact(buf, 2) != 2) // HERE 
    return(-1);     // HERE 
    len = (buf[0] << 8) | buf[1]; // HERE 
    return read_exact(buf, len); 
} 

nella funzione read_cmd e

write_cmd(byte *buf, int len) 
{ 
    byte li; 

    li = (len >> 8) & 0xff;  // HERE 
    write_exact(&li, 1);   // HERE 

    li = len & 0xff;    // HERE 
    write_exact(&li, 1);   // HERE 

    return write_exact(buf, len); 
} 

nella funzione write_cmd. Penso che la spiegazione sia trattata in 0 - a); questa è un'intestazione che indica/scopre per quanto tempo il resto del messaggio sarà (sì, questo significa che può essere solo una lunghezza finita, e che la lunghezza deve essere espressa in due byte).

4 - Non sono del tutto sicuro del motivo per cui sarebbe stato un problema. Cura di elaborare?

5 -buf è un array di byte e deve essere esplicitamente limitato (a fini di gestione della memoria, penso). Ho letto "100" qui come "un numero più grande della dimensione massima dei messaggi che intendiamo ospitare". Il numero effettivo prelevato sembra arbitrario, sembra che qualsiasi cosa 4 o superiore farebbe, ma potrei sopportare di essere corretto su quel punto.

+0

La tua ipotesi su 'buf' è corretta. È un array, cioè un'area di memoria contigua in grado di contenere elementi 'n' di un tipo specificato. In questo scenario, la memoria viene allocata nello stack. Un modo diverso di allocare la memoria sarebbe quello di rendere 'buf' un puntatore e allocarlo nell'heap usando' malloc' (ma poi si deve essere sicuri di 'liberare' la memoria da soli quando si ha finito con esso). –

+0

Riguardo a 4. Prima il tutorial dice che il programma C dovrebbe leggere da stdin (descrittore di file 0) e scrivere su stdout (descrittore di file 1). Quindi il tutorial dice che stdin/stdout non dovrebbe essere usato per la comunicazione con Erlang. "Non è una contraddizione diretta? Forniscono un esempio di programma C che usa stdin/stdout e poi dicono che non dovrebbe essere usato, perché stdin/stdout è memorizzato nel buffer.Perché mi manca qualcosa qui .. – skanatek

+1

@MartinLee - Oh, è vero, suppongo, l'ho inteso nel senso che non dovresti usare queste porte per avere una comunicazione avanti e indietro tra i processi di Erlang e C (questo è ciò che la cosa C Node è per) .Non siamo però: Erlang sta inviando una singola richiesta e si aspetta una singola risposta 'int' per invocazione del programma.Probabilmente potrei aver frainteso o letto male – Inaimathi