Weblog

All | CMT | General | NUMA | OpenSolaris | Perl | Photo | Programmers Desk | STREAMS
« Converting C arrays... | Main | Open Solaris is Good... »
20050513 Friday May 13, 2005

STREAMS Hello World

Ever wondered, what would it take to write a minimum STREAMS module that can be correctly installed onto Solaris system? Here is an example of such module (which we will call nullmod) with some explanations.

Every STREAMS module should define the following entry points:

o Module open routine;

o Module close routine;

o Module read side put procedure;

o Module write side put procedure;

o Module read side service procedure (optional);

o Module write side service procedure (optional);

Since our module will only pass messages back and forth it will use putnext(9f) as both write and read side put procedure and will not have any service procedures. So we need to define only open/close functions which we will call nullmodopen() and nullmodclose().

  /*
   * Module open routine.
   * Mark the module as "opened" and link it to
   * the STRREAM.
   */
  static int
  nullmodopen(queue_t *rq, dev_t *dev,
        int oflag, int sflag, cred_t *crp)
  {
        if (sflag != MODOPEN)
                return (EINVAL);

        /*
         * Prevent duplicate opens.
         * The q_ptr is reserved for module private use
         */
        if (rq->q_ptr != NULL)
                return (0);

        /* Mark the module as "opened" */
        rq->q_ptr = WR(rq)->q_ptr = (void *)1;

        /* Link the module into the STREAM */
        qprocson(rq);
        /*
         * At this point module is linked in the STREAM
         * and can send/receive messages.
         * Its put/service procedures may execute at any time.
         */
        return (0);
  }

  /*
   * Module close routine.
   * Disconnect the module from the STREAM.
   */
  static int
  nullmodclose(queue_t *rq)
  {
        /* Disconnect the module from the STREAM */
        qprocsoff(rq);
        /*
         * At this point module is disconnected from the STREAM and can
         * no longer receive messages. Its put or service procedures are not
         * running.
         */
        rq->q_ptr = WR(rq)->q_ptr = NULL;
        return (0);
  }

This is pretty much the only code we need to write. The rest is the glue code that should be present in any module. Here is the description of this glue code.

Every source file should start with comments, so we shall start the nullmod.c with the appropriate comment:

  /*
   * Nullmod: the minimal functioning STREAMS module.
   *
   * Copyright ..... (place your favorite one here).
   *
   */

We will need certain system include files:

  /*
   * Required include files.
   */
  #include      <sys/types.h>
  #include      <sys/conf.h>
  #include      <sys/cred.h>
  #include      <sys/ddi.h>
  #include      <sys/modctl.h>

As we discussed before, our module will define only two functions - nullmodopen() and nullmodclose():

  /*
   * Function prototypes.
   */
  static int    nullmodopen(queue_t *, dev_t *, int, int, cred_t *);
  static int    nullmodclose(queue_t *);

Every STREAMS kernel module should have a corresponding module_info(9S) structure:

  static struct module_info     nullmod_minfo = {
        1,              /* mi_idnum */
        "nullmod",      /* mi_idname */
        0,              /* mi_minpsz */
        INFPSZ,         /* mi_maxpsz */
        0,              /* mi_hiwat */
        0               /* mi_lowat */
  };

Also it should have qinit(9S) structures for both the read and write sides:

  static struct qinit nullmod_rinit = {
        (int (*)())putnext,     /* qi_putp */
        NULL,           /* qi_srvp  */
        nullmodopen,    /* qi_qopen */
        nullmodclose,   /* qi_qclose */
        NULL,           /* qi_qadmin */
        &nullmod_minfo, /* qi_minfo */
  };

  static struct qinit nullmod_winit = {
        (int (*)())putnext,     /* qi_putp */
        NULL,           /* qi_srvp */
        NULL,           /* qi_qopen */
        NULL,           /* qi_qclose */
        NULL,           /* qi_qadmin */
        &nullmod_minfo, /* qi_minfo */
  };

The streamtab(9S) structure links both together:

  static struct streamtab nullmod_info = {
        &nullmod_rinit, /* st_rdinit */
        &nullmod_winit, /* st_wrinit */
  };

The fmodsw(9S) structure describes tqhe module to the operating system, providing the pointer to the streamtab structure above:

  static struct fmodsw fsw = {
        "nullmod",      /* module name */
        &nullmod_info,  /* streams information */
        D_MP            /* module flags - multithreaded module */
  };

Now we need to provide the linkage information for the module:

  /*
   * Module linkage information for the kernel.
   */
  struct mod_ops mod_strmodops;

  static struct modlstrmod modlstrmod = {
        &mod_strmodops, "Example pass-through module 1.0", &fsw
  };

  static struct modlinkage modlinkage = {
        MODREV_1, (void *)&modlstrmod, NULL
  };

Every loadable kernel module should also provide _init, _fini and _info entry points.

_init(9E) initializes a loadable module. It is called before any other routine in a loadable module. Most modules do not require any specific initialization and can just call mod_install(9F). _fini(9E) prepares a loadable module for unloading. It is called when the system wants to unload a module. In most cases it can just call mod_remove(9F). _info(9E) returns information about a loadable module. In most cases _info(9E) just returns the value returned by mod_info(9F).

  /*
   * Standard module entry points.
   */
  int _init(void)
  {
        return (mod_install(&modlinkage));
  }

  int _fini(void)
  {
        return (mod_remove(&modlinkage));
  }

  int_info(struct modinfo *modinfop)
  {
        return (mod_info(&modlinkage, modinfop));
  }

Now we need to put our definitions for nullmodopen() and nullmodclose() above and we are done! Our ``do-nothing'' module is ready.

Let us save the module in the file nullmod.c. To compile it we need a C compiler which can generate the code for the native kernel mode - 32-it for x86 and 64-bit for Sparc or AMD64 platforms. If we are using Sun compilers we can use the following commands to produce the module binary on sparc:

  cc -c  -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL -xarch=v9 nullmod.c

Or with gcc

  gcc -c -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL -m64 nullmod.c

After that we need to use ld with -r option to produce the final binary:

  ld -r -o nullmod nullmod.o

Now we need to copy our module to /usr/kernel/strmod/sparcv9 (you need to be a super-user for this):

  # cp nullmod /usr/kernel/strmod/sparcv9

!!WARNING!!: you are going to install the kernel module. It is possible that the system will panic if there is some problem in the module. Please make sure that no one else is using the system and you will not upset anyone (including yourself) if the system panics as a result of your experiments. End of WARNING.

Now we can load the module:

  # modload nullmod

And verify that it is loaded:

  # modinfo | grep nullmod
  147 7bbbde30 320 - 1 nullmod (Example pass-through module 1.0)

We can also insert our module onto STDIN for our shell:

  # strchg -h nullmod

and verify that it is present:

  # strconf 
  nullmod
  ttcompat
  ldterm
  ptem
  pts

When we are done, we can remove the module from our shell STDIN:

  # strchg -p
  # strconf 
  ttcompat
  ldterm
  ptem
  pts

You can also unload the module (it is usually not rquired as Solaris unloads unused modules itself):

  # modunload -i 147
  # modinfo|grep nullmod
  #

Now you know how to

- Write a trivial Solaris STREAMS kernel module
- Compile the module
- Install the module on your system
- Load and unload the module

Why this module may be of any use? I can think of several reasons:

It provides a good template for more complicated modules

You can use it to estimate performance impact of inserting a module in a STREAM. This is the fastest module possible, so any additional code will only slow things down.

You can demonstrate your Solaris kernel skills duuring the job interview.

Not bad for a simple do-nothing program!

( May 13 2005, 09:06:15 PM PDT ) Permalink Comments [3]

Trackback URL: http://blogs.sun.com/akolb/entry/streams_hello_world
Comments:

Excellent mini-guide. Thanks, so much! I honestly wish I had more of these cool examples when I was weaving through the "STREAMS Programming Guide" to write my own modules :-)

Posted by Giorgos Keramidas on May 15, 2005 at 07:53 AM PDT #

I plan to add more examples like this. Please let me know if some topic is of specific interest for you.

Posted by Alexander Kolbasov on May 16, 2005 at 12:53 PM PDT #

What is the purpose of the final 'ld -r' when linking the module? Is not the 'nullmod.o' file already a relocatable object?

Posted by vm on June 23, 2005 at 10:13 AM PDT #

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed

Calendar

RSS Feeds

Search

Links

Navigation

Referers