(This is something I did in December but have only just gotten round to posting about)
The OCaml Unix module provides many of the system calls needed for day-to-day work, but occasionally there is one that is only available in C. In a recent project, I needed to find a PID from a semaphore ID when parsing the output of:
$ ipcs -s ------ Semaphore Arrays -------- key semid owner perms nsems 0xd69fd280 1638401 oracle 660 504 <snip>
To interface a C library with an OCaml program there are three elements:
- The interface file which maps between C and OCaml function names
- The stub file which wraps C functions in OCaml datatypes
- The actual OCaml program itself that does the work
Here I will show a stripped down example of doing this for semctl()
. First the interface file semctl.mli
:
external semctl: int -> int = "caml_semctl"
This is very simple, as in this example we are only using the one function. It takes an int
as an argument and returns another int
. Next the stub file semctl_stub.c
:
#include <sys/sem.h> #include <caml/mlvalues.h> #include <caml/memory.h> #include <caml/alloc.h> #include <caml/custom.h> value caml_semctl(value semid) { CAMLparam1(semid); CAMLreturn(Val_int(semctl(Int_val(semid), 0, GETPID))); }
Notice the name from the interface file is prefixed caml_
to avoid clashing with the actual C function, because I still want to call it by its C name in the main OCaml program. The parameter is cast from OCaml→C type and the return value cast back again (all on the stack). Finally the main OCaml program semctl_getpid.ml
:
open Semctl open Unix open Printf let () = match (Array.length Sys.argv) with | 2 -> print_endline (sprintf "%d\n" (semctl (int_of_string Sys.argv.(1)))) | _ -> prerr_endline (sprintf "Usage: %s <semid> " Sys.argv.(0))
To compile and link these three components:
$ ocamlc -c semctl.mli $ ocamlc -c semctl_stub.c $ ocamlc -custom -o semctl_getpid unix.cma semctl_getpid.ml semctl_stub.o
Then running it:
$ ./semctl_getpid 1638401 9022 $ ps -e|grep 9022 9022 ? 00:00:03 sp_cop
Exactly as expected, one of the components of Shareplex. Anyway, knowing this technique moves me a little closer to my eventual goal of maintaining my own native Oracle bindings.
You should add this to the ExtUnix library: http://extunix.forge.ocamlcore.org/
Ah interesting, I did not know about this