Search code examples
cerlangelixirfreebsderlang-otp

How to implement Erlang Driver As Default efficient Implementation


Erlang Run-Time System (ERTS) have a few drivers written in C language that used to interact with the OS or to access low-level resources, In my knowledge the ERTS compile these drivers at boot time to get ready for loading from Erlang code, the driver inet_drv.c is one of these drivers and it's used to handle networking tasks like creating sockets and listening or accepting new incoming connections.

I wanted to test this driver manually to get a general view of the default behaviour of the ERTS and to know how to implement drivers efficiently in the future, I tracked the Erlang Manual Reference to implement drivers that said: first write and compile the driver by an OS C Language Compiler, second load the driver from erlang code using erl_ddll module, finally link to the driver by a spawned Erlang process, so this is very simple and easy.

So I tried these steps with the driver inet_drv.c, I searched for it and tried to compile it with Clang Compiler which is the Default C Compiler of FreeBSD System :

cc inet_drv.c

after that there was an error saying that the file erl_driver.h is not defined, this header file is used in the driver's code as an included file (#include<erl_driver.h>) so I searched for it and add it's directory path to the cc command using the -I option to get the compiler search for the included file in this directory and I recompile it :

cc inet_drv.c -I/usr/ports.... 

after that, there was be another undefined file so I did the same thing for 5 or 6 times and finally, I add all needed paths for included files and the result is this command :

cc inet_drv.c

-I/usr/ports/lang/erlang/work/otp-OTP-21.3.8.18/erts/emulator/beam

-I/usr/local/lib/erlang/usr/include

-I/usr/ports/lang/erlang/work/otp-OTP-21.3.8.18/erts/emulator/sys/unix

-I/usr/ports/lang/erlang/work/otp-OTP-21.3.8.18/erts/include/internal

-I/usr/ports/lang/erlang/work/otp-OTP-21.3.8.18/erts/emulator/sys/common

-I/usr/ports/lang/erlang/work/stage/usr/local/lib/erlang/erts-10.3.5.14/include/internal

I was surprised by the result:13 errors and 7 warnings, the shell output and errors and warnings description are in the links below. My question is : why these errors occurs ? What is the wrong in what I did ?

Since this driver works perfectly in response to the ERTS networking tasks, then it's compiled by the ERTS without errors and the ERTS should use an OS C Language Compiler which is Clang by default and should add included headers files as I did, so why this did not work when I tried to do ?

https://ibb.co/bbtFHZ7

https://ibb.co/sF8QsDx

https://ibb.co/Lh9cDCH

https://ibb.co/W5Gcj7g


Solution

  • First things first:

    In my knowledge the ERTS compile these drivers at boot time

    No, ERTS doesn't compile the drivers. inet_drv.c is compiled as part of Erlang/OTP and linked into the beam.smp binary.

    inet_drv is not a typical driver. Quoting the How to Implement a Driver section of the documentation:

    A driver can be dynamically loaded, as a shared library (known as a DLL on Windows), or statically loaded, linked with the emulator when it is compiled and linked. Only dynamically loaded drivers are described here, statically linked drivers are beyond the scope of this section.

    inet_drv is a statically loaded driver, and as such doesn't need to be loaded with erl_ddll.


    On to the compilation errors. All the compiler parameters are automatically added for you when you run make, so if you need to call the compiler manually, better just check the command line that make generated and start from that. Let's look at the build log for the Debian Erlang package. Searching for inet_drv we get this command line (line breaks added):

    x86_64-linux-gnu-gcc -Werror=undef -Werror=implicit -Werror=return-type  -fno-common \
      -g -O2 -fno-strict-aliasing -I/<<PKGBUILDDIR>>/erts/x86_64-pc-linux-gnu    -D_GNU_SOURCE \
      -DHAVE_CONFIG_H -Wall -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes \
      -Wdeclaration-after-statement -DUSE_THREADS -D_THREAD_SAFE -D_REENTRANT -DPOSIX_THREADS \
      -D_POSIX_THREAD_SAFE_FUNCTIONS   -DBEAMASM=1 -DLIBSCTP=libsctp.so.1 \
      -Ix86_64-pc-linux-gnu/opt/jit -Ibeam -Isys/unix -Isys/common -Ix86_64-pc-linux-gnu \
      -Ipcre -I../include -I../include/x86_64-pc-linux-gnu -I../include/internal \
      -I../include/internal/x86_64-pc-linux-gnu -Ibeam/jit -Ibeam/jit/x86 -Idrivers/common \
      -Idrivers/unix -c \
      drivers/common/inet_drv.c -o obj/x86_64-pc-linux-gnu/opt/jit/inet_drv.o
    

    Some of it will be different since you're building on FreeBSD, but the principle stands - most of the time you'll want to just run make instead of invoking the compiler directly, but if you need to invoke the compiler, it will be much easier to start with the command line that make generated for you.