8

免責事項:質問の作成者は、アーランの平均的な知識とCの基本的な知識を持っています。

現在、相互運用性チュートリアルのユーザーガイドを読んでいます。例を正常にコンパイルしましたcomplex.cが、Erlangポートで問題なく動作します。

ただし、実際のCコードがどのように機能するかを理解したいと思います。私はそれを一般的に理解しています。この例では、標準入力から2バイトを読み取り、最初のバイトをチェックします。最初のバイトに応じて、fooまたはbar関数を呼び出します。これが今の私の理解の限界です。

したがって、両方を取る場合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);
}

および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);
  }
}

各関数は実際にそこで何をしますか?li, len, i, wrote, got変数は実際にどのような目的を果たしますか?

さらに小さな質問:

  1. なぜ関数はvoidsでさえも戻り型を持たないのですか?
  2. ErlangポートがCにデータを送信するとき、最初のバイトが呼び出される関数を決定します。バイトが10進数の1を保持している場合は、foo()が呼び出され、バイトが10進数の2を保持している場合は、bar()が呼び出されます。とにかく変更されていない場合は、このプロトコルを使用して、それぞれ1つのパラメーターのみで最大255の異なるC関数を呼び出すことができます。そうですか?
  3. 「長さインジケーターの追加はErlangポートによって自動的に行われますが、外部Cプログラムで明示的に行う必要があります」。どういう意味ですか?コードのどの行で実行されますか?
  4. チュートリアルから:「デフォルトでは、Cプログラムは標準入力(ファイル記述子0)から読み取り、標準出力(ファイル記述子1)に書き込む必要があります。」次に、「stdinとstdoutはバッファリングされた入力/出力用であり、Erlangとの通信には使用しないでください!」ここでのキャッチは何ですか?
  5. なぜbufに初期化されるの[100]ですか?
4

1 に答える 1

6

この回答も同様に否認されます(私はErlangまたはCプログラマーではなく、たまたま同じ資料を使用しています)

最初のモデルは少しずれています。コードが実際に機能する方法は、実際のメッセージの長さを示していると想定して、から最初の2バイトをstdin読み取り、次に。からさらに多くのバイトを読み取ることstdinです。この特定のケースでは、実際のメッセージは常に2バイト(関数に対応する数値とそれに渡す単一の整数引数)であることが発生します。

0-a) からバイトを読み取り、最初にread_exact読み取るlenバイト数(最初の2バイトで示される数値、または使用可能なバイトが2バイト未満の場合はnone)を決定し、次にそのバイト数を読み取ります。バイトをに書き込み、2バイト長のヘッダーを出力するために使用し、その後に適切な長さのメッセージ(できれば)が続きます。stdinread_cmdread_exactwrite_exactlenstdoutwrite_cmdwrite_exact

0-b)len上記で十分にカバーされていると思います。liは、書き込み関数の2バイトヘッダーを生成するために使用される変数の名前です(ビットシフト操作を段階的に説明することはできませんが、最終的には、len送信された最初の2バイトで表されます)。iは中間変数であり、その主な目的はエラーを返さないことを確認することであると思われます(エラーが発生した場合、そのエラーコードは/の結果として返されwriteます)。書き込まれた/読み取られたバイト数を追跡​​します。含まれているループは、が大きくなる前に終了します。readread_exactwrite_exactwrotegotlen

1-実際にはわかりません。私が使用していたバージョンはタイプintですが、それ以外は同じです。私はあなたがリンクしているガイドではなく、ProgrammingErlangの第12章から私のものを得ました。

2-それは正しいですが、ポートプロトコルのポイントは、異なる引数を送信するように変更できることです(任意の引数を送信する場合は、ポートではなくCノードメソッドを使用する方がよいでしょう)。例として、最近の記事で微妙に変更して、単一の文字列を送信するようにしました。これは、C側で呼び出したい関数が1つしかないため、関数を指定する必要がないためです。また、Cで記述された255を超える異なる操作を呼び出す必要があるシステムがある場合は、その構造を再考することをお勧めします(または、9つすべてをCで記述します)。

3-これは行われます

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);
}

read_cmd関数と

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);
}

write_cmd関数内。0 - a)説明は;でカバーされていると思います。これは、メッセージの残りの長さを通知/検出するヘッダーです(はい、これは、メッセージが有限の長さである可能性があり、その長さは2バイトで表現可能でなければならないことを意味します)。

4-なぜそれがここでのキャッチになるのか完全にはわかりません。気になりますか?

5- bufはバイト配列であり、明示的に制限する必要があります(メモリ管理の目的で)。100ここでは、「収容する予定の最大メッセージサイズよりも大きい数」と読みました。実際に選んだ数は恣意的で、4以上なら何でもいいようですが、その点で修正される可能性があります。

于 2012-05-08T02:00:52.673 に答える