1

MIT-SHM extensionのバインディングでOCaml-Xlibを拡張しようとしています。CとOCamlのインターフェースを試みるのはこれが初めてで、Cで何も書いたことがないので、どこかで愚かなことをしていると思います。

最初に XShmQueryExtension 関数を追加しました。Xlib.ml ファイルに以下を追加しました

external xShmQueryExtension: dpy:display -> bool = "ml_xShmQueryExtension"

以下を wrap_xlib.c ファイルに追加

CAMLprim value
ml_xShmQueryExtension( value dpy )
{
  int ans = XShmQueryExtension( Display_val(dpy) );
  return Val_bool(ans);
}

Makefile を Xext とリンクするように変更したところ、動作しました。OCaml から xShmQueryExtension 関数を呼び出すと、true が返されました。

現在、共有 xImage を作成し、共有メモリを初期化し、それを X サーバーに接続する関数を作成しようとしています。Xlib.ml ファイルに以下を追加しました。

type xShmSegmentInfo

external xShmCreateImageAndAttach:
  dpy:display -> visual:visual -> depth:int -> fmt:ximage_format
  -> width:uint -> height:uint -> xShmSegmentInfo * xImage
    = "ml_xShmCreateImageAndAttach_bytecode"
      "ml_xShmCreateImageAndAttach"

そして以下を wrap_xlib.c ファイルに追加します。

#define Val_XShmSegmentInfo(d) ((value)(d))
#define XShmSegmentInfo_val(v) ((XShmSegmentInfo *)(v))

CAMLprim value
ml_xShmCreateImageAndAttach( value dpy, value visual, value depth, value format,
                             value width, value height)
{
    CAMLparam5(dpy, visual, depth, format, width);
    CAMLxparam1(height);
    CAMLlocal1(ret);

    XShmSegmentInfo *shminfo = malloc(sizeof(XShmSegmentInfo));

    XImage *ximage = XShmCreateImage(
        Display_val(dpy),
        Visual_val(visual),
        Int_val(depth),
        XImage_format_val(format),
        NULL,
        shminfo,
        UInt_val(width),
        UInt_val(height)
    );
    shminfo->shmid = shmget (IPC_PRIVATE,
      ximage->bytes_per_line * ximage->height, IPC_CREAT|0777);
    shminfo->shmaddr = ximage->data = (char *) shmat (shminfo->shmid, 0, 0);
    if (shminfo->shmaddr == -1)
      fprintf(stderr,"Error");
    shminfo->readOnly = False;
    XShmAttach (Display_val(dpy), shminfo);

    ret = caml_alloc(2, 0);
    Store_field(ret, 0, Val_XShmSegmentInfo(shminfo) );
    Store_field(ret, 1, Val_XImage(ximage) );
    CAMLreturn(ret);
}

CAMLprim value
ml_xShmCreateImageAndAttach_bytecode( value * argv, int argn )
{
    return ml_xShmCreateImageAndAttach(argv[0], argv[1], argv[2], argv[3],
                                       argv[4], argv[5]);
}

今、私は自分の OCaml プログラムでこの関数を呼び出しています:

let disp = xOpenDisplay ""
let screen = xDefaultScreen disp
let (shminfo, image) = xShmCreateImageAndAttach disp
  (xDefaultVisual disp screen)
  (xDefaultDepth disp screen) ZPixmap 640 174

これは私の OCaml プログラムのトップレベル呼び出しであり、変数 shminfo と image を再び使用することはありません (これは、関数が機能することをテストするためだけです)。この呼び出しは失敗しませんが、しばらくするとプログラムの segfault が発生します (プログラムの残りの部分は常に xGetImage で画面をダンプし、ピクセルを操作します。呼び出しとは関係のない xGetPixel で segfault が発生します)。上記の xShmCreateImageAndAttach)。行を削除するとshminfo->shmaddr = ximage->data = (char *) shmat (shminfo->shmid, 0, 0);、セグメンテーション違反が発生しなくなることに気付きました (ただし、もちろん、これは私が望むことにはなりません)。

これはガベージコレクターと何らかの関係があると思いますが、修正方法がわかりません。OCamlのドキュメントに、mallocで取得したポインタを値型にキャストすることについての警告が出ているのですが、意味がよくわからず、関係あるかどうかわかりません。

編集:

次の2行を次のように置き換えましたshmat

fprintf(stderr,"%i\n",(int)shminfo->shmaddr);
fflush(stderr);

のようなものが得られる1009700864ので、への呼び出しが機能してshmatいるようです。

gdb によって与えられたバックトレースは次のとおりです。

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7acdde8 in ?? () from /usr/lib/libX11.so.6
(gdb) backtrace
#0  0x00007ffff7acdde8 in ?? () from /usr/lib/libX11.so.6
#1  0x000000000044070c in ml_XGetPixel ()
#2  0x00000000004165b9 in camlInit__rvb_at_1023 () at init.ml:43
#3  0x0000000000415743 in camlParse__find_guy_1046 () at parse.ml:58
#4  0x000000000041610c in camlParse__pre_parse_1044 () at parse.ml:95
#5  0x0000000000415565 in camlGame__entry () at game.ml:26
#6  0x00000000004141f9 in caml_program ()
#7  0x000000000045c03e in caml_start_program ()
#8  0x000000000044afa5 in caml_main ()
#9  0x000000000044afe0 in main ()
4

1 に答える 1

2

この警告は、型にキャストしfree()ているshminfoポインターをX が呼び出そうとする場合に関連します。value問題は、OCaml が値を自由にコピーして後で GC で処理できると想定していることです。これはポインター値には当てはまらないため、ポインターのぶら下がりコピーが存在する可能性があります。また、スペースは OCaml のヒープの一部として再利用される可能性があり、実際に問題が発生します。

X がこれを行うとfree()は思えませんし、コードを呼び出していないので、これが問題だとは思いません。X がどのように機能するかはわかりません。

fflush(stderr)に電話した後に電話するといいかもしれませんfprintf()。おそらく何も変わらないでしょうが、トレース メッセージがバッファリングされる傾向があり、プログラムがクラッシュしたときに表示されないことがわかりました。

また、セグメンテーション違反のアドレスがどのように見えるかを知っておくとよいでしょう。0に近いですか?それとも、ヒープの真ん中にある大きなアドレスですか?

申し訳ありませんが、エラーを特定できません。Display_valコードを 4 ~ 5 回読んだ後、残りが正しく機能していると仮定して、間違っていることは何も見当たりません。しかし、これを正しく行うのは難しいです。

于 2012-11-22T16:53:42.013 に答える