Erlang 21 - 8. NIFs

8 एनआईएफ




erlang

8 एनआईएफ

यह अनुभाग मूल Problem Example कार्यान्वित क्रियाओं (NIF) का उपयोग करके Problem Example में Problem Example को हल करने के तरीके का Problem Example है।

NIF को एक प्रायोगिक विशेषता के रूप में Erlang / OTP R13B03 में पेश किया गया था। यह पोर्ट ड्राइवरों का उपयोग करने की तुलना में सी-कोड को कॉल करने का एक सरल और अधिक कुशल तरीका है। एनआईएफएस सिंक्रोनस फ़ंक्शंस के लिए सबसे उपयुक्त हैं, जैसे कि उदाहरण में foo और bar , जो साइड इफेक्ट के बिना कुछ अपेक्षाकृत कम गणना करते हैं और परिणाम वापस करते हैं।

एनआईएफ एक ऐसा कार्य है जो एर्लांग के बजाय सी में लागू किया जाता है। एनआईएफ कॉल करने वालों को किसी भी अन्य कार्य के रूप में दिखाई देते हैं। वे एक मॉड्यूल से संबंधित हैं और किसी भी अन्य एर्लांग कार्यों की तरह कहा जाता है। एक मॉड्यूल के एनआईएफ को एक गतिशील लोड करने योग्य, साझा पुस्तकालय (यूएनआईएक्स में एसओ, विंडोज में डीएलएल) से जोड़ा जाता है। मॉड्यूल के एर्लैंग कोड द्वारा NIF लाइब्रेरी को रनटाइम में लोड किया जाना चाहिए।

एनआईएफ लाइब्रेरी को गतिशील रूप से एमुलेटर प्रक्रिया में जोड़ा जाता है, यह एरलांग (पोर्ट ड्राइवरों के साथ) से सी-कोड को कॉल करने का सबसे तेज़ तरीका है। NIF को कॉल करने के लिए किसी संदर्भ स्विच की आवश्यकता नहीं होती है। लेकिन यह सबसे कम सुरक्षित भी है, क्योंकि एक एनआईएफ में एक दुर्घटना एमुलेटर को भी नीचे लाती है।

8.1 एरलांग कार्यक्रम

भले ही एक मॉड्यूल के सभी कार्य NIF हैं, फिर भी दो कारणों से एक एरलैंग मॉड्यूल की आवश्यकता है:

  • NIF लाइब्रेरी को समान मॉड्यूल में स्पष्ट रूप से Erlang कोड द्वारा लोड किया जाना चाहिए।
  • एक मॉड्यूल के सभी एनआईएफ में एक एर्लैंग कार्यान्वयन भी होना चाहिए।

आम तौर पर ये न्यूनतम स्टब कार्यान्वयन हैं जो एक अपवाद को फेंकते हैं। लेकिन उनका उपयोग उन कार्यों के लिए फॉलबैक कार्यान्वयन के रूप में भी किया जा सकता है जिनमें कुछ आर्किटेक्चर पर मूल लागू नहीं होते हैं।

erlang:load_nif/2 पुस्तकालयों को erlang:load_nif/2 कहकर लोड किया जाता है, साझा लाइब्रेरी का नाम तर्क के रूप में है। दूसरा तर्क किसी भी शब्द का हो सकता है जिसे पुस्तकालय में पारित किया जाएगा और इसे आरंभीकरण के लिए उपयोग किया जाएगा:

-module(complex6).
-export([foo/1, bar/1]).
-on_load(init/0).

init() ->
    ok = erlang:load_nif("./complex6_nif", 0).

foo(_X) ->
    exit(nif_library_not_loaded).
bar(_Y) ->
    exit(nif_library_not_loaded).

यहां, on_load होने पर फंक्शन on_load को स्वचालित रूप से कॉल करने के लिए निर्देश on_load का उपयोग किया जाता है। यदि init ok अलावा कुछ भी देता है, जैसे कि जब NIF लाइब्रेरी का लोडिंग इस उदाहरण में विफल रहता है, तो मॉड्यूल अनलोड किया जाता है और इसके भीतर फ़ंक्शन के लिए कॉल विफल हो जाता है।

एनआईएफ लाइब्रेरी को लोड करना स्टब कार्यान्वयनों को ओवरराइड करता है और इसके बजाय foo और bar को एनआईएफ कार्यान्वयन के लिए भेजे जाने के लिए कॉल का कारण बनता है।

8.2 एनआईएफ लाइब्रेरी कोड

मॉड्यूल के एनआईएफ को संकलित किया जाता है और एक साझा पुस्तकालय में जोड़ा जाता है। प्रत्येक NIF को एक सामान्य C फ़ंक्शन के रूप में लागू किया जाता है। मैक्रो ERL_NIF_INIT संरचनाओं के एक सरणी के साथ मॉड्यूल में सभी एनआईएफ के नाम, धमनी और फ़ंक्शन पॉइंटर्स को परिभाषित करता है। हेडर फ़ाइल erl_nif.h को शामिल किया जाना चाहिए। जैसा कि पुस्तकालय एक साझा मॉड्यूल है, न कि एक कार्यक्रम, कोई मुख्य कार्य मौजूद नहीं है।

argc को दिए गए फ़ंक्शन आर्गुमेंट्स एक सरणी argv में दिखाई देते हैं, argc साथ सरणी की लंबाई और इस प्रकार फ़ंक्शन की arity होती है। फ़ंक्शन के Nth तर्क को argv[N-1] रूप में एक्सेस किया जा सकता है। एनआईएफ एक पर्यावरण तर्क भी लेते हैं जो एक अपारदर्शी संभाल के रूप में कार्य करता है जिसे अधिकांश एपीआई कार्यों को पारित करने की आवश्यकता होती है। पर्यावरण में कॉलिंग एरलांग प्रक्रिया के बारे में जानकारी शामिल है:

#include <erl_nif.h>

extern int foo(int x);
extern int bar(int y);

static ERL_NIF_TERM foo_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int x, ret;
    if (!enif_get_int(env, argv[0], &x)) {
	return enif_make_badarg(env);
    }
    ret = foo(x);
    return enif_make_int(env, ret);
}

static ERL_NIF_TERM bar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int y, ret;
    if (!enif_get_int(env, argv[0], &y)) {
	return enif_make_badarg(env);
    }
    ret = bar(y);
    return enif_make_int(env, ret);
}

static ErlNifFunc nif_funcs[] = {
    {"foo", 1, foo_nif},
    {"bar", 1, bar_nif}
};

ERL_NIF_INIT(complex6, nif_funcs, NULL, NULL, NULL, NULL)

यहाँ, ERL_NIF_INIT पास निम्न तर्क हैं:

  • पहला तर्क सी-पहचानकर्ता के रूप में एरलंग मॉड्यूल का नाम होना चाहिए। इसे मैक्रो द्वारा कठोर किया जाएगा।

  • दूसरा तर्क ErlNifFunc संरचनाओं का नाम है, जिसमें प्रत्येक ErlNifFunc का नाम, ErlNifFunc और फ़ंक्शन पॉइंटर है।
  • शेष तर्क कॉलबैक फ़ंक्शंस के संकेत हैं जिनका उपयोग लाइब्रेरी को इनिशियलाइज़ करने के लिए किया जा सकता है। उनका उपयोग इस सरल उदाहरण में नहीं किया जाता है, इसलिए वे NULL

फ़ंक्शन तर्क और वापसी मान ERL_NIF_TERM प्रकार के मान के रूप में ERL_NIF_TERM । यहां, enif_make_int टर्म और सी-टाइप के बीच कनवर्ट करने के लिए enif_get_int और enif_make_int जैसे कार्यों का उपयोग किया जाता है। यदि फ़ंक्शन तर्क argv[0] एक पूर्णांक नहीं है, तो enif_get_int गलत देता है, इस स्थिति में यह enif_make_badarg के साथ एक badarg धारणा को फेंककर वापस लौटता है।

8.3 उदाहरण चल रहा है

चरण 1. सी कोड संकलित करें:

unix> gcc -o complex6_nif.so -fpic -shared complex.c complex6_nif.c
windows> cl -LD -MD -Fe complex6_nif.dll complex.c complex6_nif.c

चरण 2: Erlang शुरू करें और Erlang कोड संकलित करें:

> erl
Erlang R13B04 (erts-5.7.5) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> c(complex6).
{ok,complex6}

चरण 3: उदाहरण चलाएँ:

3> complex6:foo(3).
4
4> complex6:bar(5).
10
5> complex6:foo("not an integer").
** exception error: bad argument
     in function  complex6:foo/1
        called as comlpex6:foo("not an integer")