Home >  A PROGRAMMER'S GUIDE TO THE MACH USER ENVIRONMENT Linda R. Walmer Mary R. Thompson

A PROGRAMMER'S GUIDE TO THE MACH USER ENVIRONMENT Linda R. Walmer Mary R. Thompson


 
 
 
 
 
 
 
 
 
 
 
 
 
 

               A PROGRAMMER'S GUIDE TO THE MACH USER ENVIRONMENT 

                                Linda R. Walmer

                               Mary R. Thompson 

                        Department of Computer Science

                          Carnegie-Mellon University

                             Pittsburgh, PA 15213

                                  Version of:

                               16 November 1989 
 
 
 
 
 
 
 
 
 
 
 
 

                                   ABSTRACT 

This  document is one of two tutorials designed to teach basic Mach programming

skills.  This manual demonstrates the use of the C Threads  library  primitives

in writing a multi-threaded program and the use of the Mach Interface Generator

(MIG) to generate remote procedure calls for interprocess communication. 

The reader should be familiar  with  the  basic  Mach  abstractions  of  ports,

messages,  virtual memory, tasks and threads. The introduction to the companion

document to this one, A Programmer's Guide to the Mach System  Calls,  explains

these concepts. 

Comments, suggestions and additions to this document are wlecome.

1. Introduction

  This  document  is  one  of  two  tutorials  designed  to  teach  basic  Mach

programming skills.  This manual demonstrates the use of the C Threads  library

primitives  in  writing  a  multi-threaded  program  and  the  use  of the Mach

Interface Generator (MIG) to generate remote procedure calls  for  interprocess

communication.  It  also  includes  a final section on where at CMU to find the

include files and libraries that comprise  the  Mach  environment  as  well  as

procedures   for  obtaining  these  files  and  setting  up  the  correct  user

environment. 

  The reader should be familiar with the  basic  Mach  abstractions  of  ports,

messages,  virtual  memory, tasks and threads before reading this document. The

introduction to the other tutorial document, A Programmer's Guide to  the  Mach

System Calls, explains these concepts. 

2. C Threads, A Single Master Thread Spawning Concurrent Slaves

  The  C  threads  package  is  a  runtime  library  that provides a C language

interface to a number of low level, Mach primitives for manipulating threads of

control.    The  constructs  provided  are:    forking  and joining of threads,

protection of critical regions with mutex  variables,  and  synchronization  by

means  of  condition  variables.  For  a  complete description of the C threads

package see the  C  Threads  manual  by  Cooper  and  Draves.    It  is  highly

recommended  that  a  programmer  doing  multi-threaded  applications use the C

threads routines rather than the Mach system calls. Not only is the  C  threads

package designed to provide a more natural set of primitives for multi-threaded

applications, but the thread system call interface has  been  recently  revised

and  may  not  be  completely stable.  Also, the C threads package is carefully

optimized to produce the most efficient use of the system calls. 

  The program at the end of this section is an example of how  to  structure  a

program with a single master thread which spawns a number of concurrent slaves.

The master thread waits until all the slaves have finished to exit.   A  random

number  generator is used to determine the "life" of the slave processes.  Once

created the slave processes in this  example  simply  loop  calling  a  cthread

function  making  the  processor available to other threads.  The random number

generator determines the length of this loop.  In a more useful version of this

program, each slave process would do something while looping. 

  In  order  for  the  master  thread  to determine when all of the slaves have

exited, a count variable is needed to keep  track  of  the  number  of  current

threads.    This  count  is  incremented  by the master with each creation of a

slave.  Each slave decrements the count when it exits.   Because  two  or  more

threads may be trying to access the count at the same time, a mutex called lock

is used to provide exclusive access to count.  If any thread  wants  to  access

the  count  variable,  it  should  first lock the mutex.  Consequently when the

mutex is locked, any thread wanting the count  variable  must  wait  until  the

mutex is unlocked. 

  Condition  variables are used to provide synchronization between threads, e.g

one thread wishes to wait until another thread has  finished  doing  something.

Every  condition  variable  is  associated with a mutex. The condition variable

represents a boolean state of the shared data that the mutex protects.  In this

example  after  all  of  the slave threads have been created, the master thread

waits until the count variable is equal to zero.  A condition variable done  is

used to represent the possibility that the count may equal zero.  Just before a

slave thread exits, it signals the condition done since  it  may  be  the  last

slave  executing.  The master thread loops waiting on the condition done.  Each

time the master thread is signalled by a condition_signal call,  it  tests  the

count for a value of zero. 

  cthread_init  is  the  first  function  called  in the example program.  This

function initializes the C threads implementation and must be called before any

of  the  other cthread functions. If a program is loaded with the /usr/mach/lib

version of crt0, this call is no longer necessary as it has already  been  done

by  crt0  (or gcrt0 or moncrt0).  The count which represents the current number

of slaves is set to zero.  mutex_alloc is called to allocate a  mutex  assigned

to  the  variable lock.  condition_alloc is used to allocate a condition object

assigned to the variable done.  The last initialization call is to  the  random

number generator. 

  After  initialization,  the  master thread loops creating the number of slave

processes desired and incrementing count with each  creation.    mutex_lock  is

called  at  the beginning of the loop.  This call results in either locking the

variable lock, or blocking until lock is unlocked by some other  thread.    The

return  of mutex_lock signals that the master can now change the variable count

knowing that no other thread will be accessing this variable until  the  master

unlocks  the mutex.  count is incremented, and a slave is created.  To create a

slave, the master calls cthread_fork followed by cthread_detach.   cthread_fork

creates  a  new  thread of control which executes concurrently with the master.

cthread_fork takes as a parameter  a  function  which  the  new  thread  is  to

execute.  Since the master does not intend to later rendez-vous with the slave,

cthread_detach is called.  Once  the  master  has  incremented  the  count  and

created  a  slave, mutex_unlock is called to give the other threads a chance to

lock the mutex and consequently access the count variable. 

  Having created the desired number of slaves, the master thread stops  looping

and waits for all of the slave threads to finish execution.  The variable count

signals the number of slave processes still executing.  mutex_lock is called so

the  master  may  safely access the count variable.  Now the master thread must

wait on the condition done by calling condition_wait.   condition_wait  unlocks

the  mutex  and  suspends  the  master,  letting other threads change the count

variable.  When condition_wait returns, the mutex is automatically locked.  The

master  resumes  and  checks  the  count to see if it is in fact equal to zero.

Since there is no gaurantee that the condition will be true when the master  is

resumed,  the condition_wait is called in a loop ending when the count is zero.

Before exiting the master  calls  mutex_unlock.    cthread_exit  is  called  to

terminate the master thread. 

  When  the  new slave was created via cthread_fork, it was given a function to

execute and one parameter to pass to that function.  In our example, the  slave

function  is given a random number as a parameter.  The slave loops this number

of times calling a function cthread_yield, which yields the processor to  other

threads.    When finished looping, the thread must decrement the count variable

because it is about to exit.  In order to safely access the  count,  mutex_lock

is  called.    Once  the  mutex lock is locked, the count is decremented.  Next

condition_signal is called to indicate that the condition  represented  by  the

condition variable done may be true.  The slave calls mutex_unlock and exits. 
 
 

2.1. Initializing the C Threads Package

  This  initialization  function  must  be  called  before  any  other C Thread

functions.  This call is now called automatically by crt0, but  multiple  calls

to this routine are harmless. 

    cthread_init(); 
 
 
 

2.2. Allocation of a Mutex Variable

  mutex_alloc provides dynamic allocation of a mutex variable. 

    mutex_t lock;      /* mutual exclusion for count */ 

    lock = mutex_alloc(); 
 
 
 

2.3. Locking a Mutex Variable

  mutex_lock  attempts  to  lock  the  given  mutex  variable.  If the mutex is

already locked this call blocks until  the  mutex  is  unlocked.    If  several

threads  attempt to lock the same mutex concurrently, one will succeed, and the

others will block until the mutex is unlocked.  A deadlock will result  from  a

thread attempting to lock a mutex it has already locked. 

    mutex_t lock;      /* mutual exclusion for count */ 

    mutex_lock(lock); 
 
 
 

2.4. Unlocking a Mutex Variable

  mutex_unlock unlocks the mutex giving other threads a chance to lock it. 

    mutex_t lock;      /* mutual exclusion for count */ 

    mutex_unlock(lock); 
 
 
 

2.5. Allocation of a Condition Variable

  condition_alloc provides dynamic allocation of a condition variable. 

    condition_t done;  /* signalled each time a slave finishes */ 

    done = condition_alloc(); 
 
 
 

2.6. Waiting on a Condition

  This  function  unlocks  the  mutex  it  takes as a parameter, suspending the

calling  thread  until  another  thread  calls  condition_signal  on  the  same

condition variable.  The mutex is then locked and the thread resumes.  There is

no guarantee that the condition will be true when the thread resumes, therefore

condition_wait should always be used in the form below. 

    mutex_t lock;

    condition_t done; 

    mutex_lock(lock);

    ...

    while (count != 0)

            condition_wait(done, lock);

    ...

    mutex_unlock(lock); 
 
 
 

2.7. Signalling a Condition

  condition_signal  is  called  when  one  thread  wishes  to indicate that the

condition represented by the condition variable  may  now  be  true.    If  any

threads  are waiting via condition_wait, at least one of them will be awakened.

If no threads are waiting, nothing happens. 

    condition_t done;  /* signalled each time a slave finishes */ 

    condition_signal(done);

2.8. Forking a C Thread

  This function takes two parameters: a function for the new thread to execute,

and a parameter to this function.  cthread_fork creates a new thread of control

in which the specified function is  executed  concurrently  with  the  caller's

thread.    This is the sole means of creating new threads.  A parameter that is

larger than a pointer  must  be  passed  by  reference.    Similarly,  multiple

parameters  must  be  simulated  by passing a pointer to a structure containing

several components.  The call to cthread_fork returns a thread identifier  that

can  be  passed to cthread_join or cthread_detach.  Every thread must be either

joined or detached exactly once. 

    /*  slave is a function that expects an integer parameter  */

    /*  see Detaching a C Thread for description of chtread_detach  */ 

    cthread_detach(cthread_fork(slave, random() % 1000)); 
 
 
 

2.9. Detaching a C Thread

  cthread_detach is used to indicate that a thread will never  be  joined.    A

thread  may  be  detached  at  any time after it is forked, as long as no other

attempt at joining or detaching has been made.  In the example  below,  at  the

time  the  thread  was  forked,  it was known that it would never be joined and

therefore it was detached. 

    /*  slave is a function that expects an integer parameter  */

    /*  see Forking a C Thread for description of chtread_fork  */ 

    cthread_detach(cthread_fork(slave, random() % 1000)); 
 
 
 

2.10. Yielding the Processor to other Threads

  This procedure is a hint to the scheduler, suggesting that this  would  be  a

convenient  point  to  schedule another thread to run on the current processor.

Calls to cthread_yield are unnecessary in  an  implementation  with  preemptive

scheduling,  but  may  be  required  to  avoid  starvation in a coroutine based

implementation. 

    int i, n; 

    /* n is set previously */ 

    for (i = 0; i < n; i += 1)

            cthread_yield(); 
 
 
 

2.11. Exiting a C Thread

  cthread_exit  causes  termination  of  the  calling  thread.    An   implicit

cthread_exit  occurs  when  the  top  level  function of a thread returns.  The

result parameter will be passed  to  the  thread  that  joins  the  caller,  or

discarded if the caller is detached. 

    cthread_exit(0);

2.12. Example V, masterslave.c

/*

* This program is an example of a master thread spawning a number of

* concurrent slaves.  The master thread waits until all of the slaves have

* finished to exit.  Once created a slave process doesn't do much in this

* simple example except loop.  A count variable is used by the master and

* slave processes to keep track of the current number of slaves executing.

* A mutex is associated with this count variable, and a condition variable

* with the mutex.  This program is a simple demonstration of the use of

* mutex and condition variables.

*/ 

#include <stdio.h>

#include <cthreads.h> 

int count;         /* number of slaves active */

mutex_t lock;      /* mutual exclusion for count */

condition_t done;  /* signalled each time a slave finishes */ 

extern long random(); 
 
 
 

init()

{

    cthread_init();

    count = 0;

    lock = mutex_alloc();

    done = condition_alloc();

    srandom(time((int *) 0));  /* initialize random number generator */


 
 
 

/*

* Each slave just counts up to its argument, yielding the processor on

* each iteration.  When it is finished, it decrements the global count

* and signals that it is done.

*/

slave(n)

    int n;

{

    int i; 

    for (i = 0; i < n; i += 1)

        cthread_yield();

    mutex_lock(lock);

    count -= 1;

    printf("Slave finished %d cycles.\n", n);

    condition_signal(done);

    mutex_unlock(lock);


 
 
 

/*

* The master spawns a given number of slaves and then waits for them all to

* finish.

*/

master(nslaves)

    int nslaves;

{

    int i; 

    for (i = 1; i <= nslaves; i += 1) {

        mutex_lock(lock);

        /*

         * Fork a slave and detach it,

         * since the master never joins it individually.

         */

        count += 1;

        cthread_detach(cthread_fork(slave, random() % 1000));

        mutex_unlock(lock);

    }

    mutex_lock(lock);

    while (count != 0)

        condition_wait(done, lock);

    mutex_unlock(lock);

    printf("All %d slaves have finished.\n", nslaves);

    cthread_exit(0);


 
 
 

main()

{

    init();

    master((int) random() % 16);  /* create up to 15 slaves */

}

3. MIG - The Mach Interface Generator

  Much  multi-task communication takes the form of one or more tasks requesting

services or responses from another task.  This in fact is a  description  of  a

Mach server process.  Since the creation and reading of messages requires a lot

of repetitious code, it should come as no great surpise that  Mach  provides  a

compiler  to  produce a remote procedure call interface to IPC message passing.

A complete description of MIG including an example of its use can be  found  in

MIG - the Mach Interface Generator by Draves, Jones and Thompson. 

  A  brief  example of the use of MIG follows. The problem that is to be solved

is to write a simple server that will return  either  a  random  integer  or  a

random string. The user interface to this server is to consist of two calls: 

    ret_code = get_random(server_port,num)

            port_t  server_port;

            int     num; 

    ret_code = get_secret(server_port,password)

            port_t  server_port;

            string25 password; 
 
 

3.1. MIG Definition file

  The  subsystem  implementor must first write a MIG definition file to specify

the details of the procdure arguments  and  the  messages  to  be  used.    MIG

understands  different  kinds  of  routines and many obscure options in the way

messages are to be formatted, sent and received.  But for this simple  case  it

is enough to define the name of the server, the types of the arguments that are

being used, and the routines that are desired.  The  following  MIG  definition

file will suffice to do this:

subsystem random 500; 

type int        = MSG_TYPE_INTEGER_32;

type port_t     = MSG_TYPE_PORT;

type boolean_t  = MSG_TYPE_INTEGER_32;

type string25   = (MSG_TYPE_STRING_C,8*25); 

import "random_types.h"; 

routine get_random(

                requestport     server_port     : port_t;

                out             num             : int); 

routine get_secret(

                requestport     server_port     : port_t;

                inout           password        : string25); 

  The first line of the definition file states that the name of the subsytem is

to be random and the messages that are created will start with the  message  id

of  500.  This  interface  will  send  four  different  messages:  one  for the

get_random function which will have a msg_id of  500;  one  for  the  reply  to

get_random  which  will  have  a msg_id of 600; one for the function get_secret

which will have a msg_id of 501; and one for its reply which will have a msg_id

of  601.  The  msg_id  is  used  by the server to identify which message it has

received. 

  The syntax of the type declaration is to define the C  type  name  first  and

then  say  that  it is equal to an IPC type name.  The set of defined IPC types

can be found in the MIG document or in the file <sys/message.h>. For string and

unstructured types the number of bits in the type must follow the name. 

  The  import  statement  gives the name of a header file to be included in the

generated code. This header file must define any non-standard C types  used  by

the interface. In this case it consists of the following definition: 

    typedef char    string25[25]; 

  The  routine  declarations  specify the name of the routine and the order and

types of the arguments. The first argument is the port  to  which  the  message

will  be  sent.  The specification in, out or inout may precede the name of any

other parameter and specify in what direction the argument is to be passed. Any

unspecified parameter, except the first one, is assumed to be an in parameter. 

  MIG  generates  three  C  files  from  the definition file. The file random.h

defines the functions to be called by a client of  the  server  and  should  be

#included  into  code  that  calls those functions. randomUser.c is the code to

create and send the messages to the client and then wait to receive  the  reply

message.  When  the  reply message is received, any out or inout parameters are

taken out of the message and returned as function  parameters  to  the  caller.

This  file  should be linked with the user of the server. randomServer.c is the

server side of the message interface. It unpacks the request message,  calls  a

function  provided  by  the  server implementor to execute the request and then

creates the reply message.  The server implementor must write two more modules.

One  is  the  main  program  of  the  server  which  receives  a message, calls

randomServer to process the request, and then  sends  the  reply  message.  The

other module must contain the functions that execute the requests. 
 
 

3.2. Server main program

  In  this example the server waits on the special port name PORT_DEFAULT which

indicates the set of all unrestricted ports belonging to  this  task.  In  this

case  the  set consists of ServerPort and notify_port().  The thread reply port

is not included in this set because it it  still  restricted.  When  ports  are

created  they  are  restricted  by  default.  This  means they can only be used

explicitly to receive messages on. 

  This server may get some EMERGENCY_MSGs from the kernel on its  notify_port()

which  it wishes to ignore. A more complicated server might need to take action

on some of the kernel's emergency messages. 

  Note that the function mach_errormsg is a library routine  that  returns  the

string  associated  with  a  Mach  error  code.  This  function  is  defined in

mach_error.h and is included in libmach.a. 

  The following code shows a typical main loop for a MIG server.

/***************************************************

*   Main program for random server

**************************************************/

#include <stdio.h>

#include <mach.h>

#include <mach_error.h>

#include <mig_errors.h>

#include <mach/message.h>

#include <mach/notify.h>

#include <servers/netname.h> 

extern boolean_t        random_server(); 

/*********************************************************

* procedure random_run:

*   Waits for messages to server,

*   handles them, and replies to sender.

*********************************************************/

void random_run ()

{

   typedef int space[1024]; /* Maximum message size */ 

   typedef struct DumMsg

        {

           msg_header_t         head;

           msg_type_t           retcodetype;

           kern_return_t        return_code;

           space                body;

        } DumMsg; 

   kern_return_t        retcode;

   msg_return_t         msgcode;

   boolean_t            ok;

   DumMsg               *pInMsg, *pRepMsg; 

   pInMsg = (DumMsg *)malloc(sizeof(DumMsg));

   pRepMsg = (DumMsg *)malloc(sizeof(DumMsg)); 

   while (TRUE)

   {

       pInMsg->head.msg_size = sizeof(DumMsg); /* bytes */

       pInMsg->head.msg_local_port = PORT_DEFAULT; 

        /* wait to receive request from client */

       msgcode = msg_receive(&pInMsg->head,MSG_OPTION_NONE,0);

       if (msgcode != RCV_SUCCESS)

           printf("error %s in Receive, message will be ignored.\n",

                mach_error_string((kern_return_t)msgcode));

       else

       {   if (pInMsg->head.msg_type == MSG_TYPE_EMERGENCY)

           {

              if (pInMsg->head.msg_id == NOTIFY_PORT_DELETED)

                  {  /* probably the death of a client's reply */}

              else

                printf("Unexpected emergency message received: id is %d\n",

                        pInMsg->head.msg_id);

           }

          else  /* normal message */

          {

                /* call server interface module */

              ok = random_server((msg_header_t *)pInMsg,

                                   (msg_header_t *)pRepMsg); 

              if ((pRepMsg->return_code != MIG_NO_REPLY) &&

                  (pInMsg->head.msg_remote_port != PORT_NULL))

              {

                  /* sending reply message to client */

                pRepMsg->head.msg_local_port = pInMsg->head.msg_local_port;

                pRepMsg->head.msg_remote_port = pInMsg->head.msg_remote_port;

                msgcode = msg_send(&pRepMsg->head,MSG_OPTION_NONE,0);

                if ((msgcode != SEND_SUCCESS) &&

                    (msgcode != SEND_INVALID_PORT))

                               /* Probably remote process death */

                    printf("error %s at Send.\n",

                                mach_error_string((kern_return_t)msgcode));

              }

          } /* normal message */

       } /* of message handling */

     }  /* of main loop */ 


 

main()

{

   port_t       ServerPort;

   kern_return_t retcode; 

        /* add notification port to default port set */

   retcode = port_unrestrict(task_self(),task_notify());

        /* allocate a service port */

   retcode = port_allocate(task_self(), &ServerPort);

   if (retcode == KERN_SUCCESS)

   {    /* add service port to default port set */

      (void) port_unrestrict(task_self(),ServerPort);

        /* check it in so users can find it */

      retcode  = netname_check_in(name_server_port,"RandomServerPort",

                PORT_NULL,ServerPort);

   }

   if (retcode != KERN_SUCCESS)

        printf("netname_check_in of RandomServerPort failed with code %s\n",

                        mach_error_string(retcode));

   random_run ();

   printf("(* !!!!! Random server exited - give it up !!!!! *)\n");

   exit(2);


 
 

3.3. Server message dispatch code

  The file randomServer.c contains the server side code that was  generated  by

MIG.  It  exports the routine random_server that is called by the main program.

It checks the message id to determine  what  message  was  received,  and  then

unpacks  the  arguments  and  calls  the  appropriate  server  procedure.   The

following fragment shows the control logic of the dispatch routine.

boolean_t random_server(InHeadP, OutHeadP)

        msg_header_t *InHeadP, *OutHeadP;

{

        register msg_header_t *InP = InHeadP;

        register death_pill_t *OutP = (death_pill_t *) OutHeadP; 

        /* set up legal return message with failure code */ 

        OutP->Head.msg_local_port = InP->msg_local_port;

        OutP->Head.msg_remote_port = InP->msg_remote_port;

        OutP->Head.msg_id = InP->msg_id + 100;

        OutP->Head.msg_type = InP->msg_type;

        OutP->Head.msg_size = sizeof *OutP; 

        OutP->RetCodeType.msg_type_name = MSG_TYPE_INTEGER_32;

        OutP->RetCodeType.msg_type_size = 32;

        OutP->RetCodeType.msg_type_number = 1;

        OutP->RetCodeType.msg_type_inline = TRUE;

        OutP->RetCodeType.msg_type_longform = FALSE;

        OutP->RetCodeType.msg_type_deallocate = FALSE; 

        OutP->RetCode = MIG_BAD_ID; 

        if ((InP->msg_id > 501) || (InP->msg_id < 500))

                return FALSE;

         else {

                static novalue (*(routines[]))() = {

                        _Xget_random,

                        _Xget_secret,

                }; 

                if (routines[InP->msg_id - 500])

                        (routines[InP->msg_id - 500]) (InP, &OutP->Head);

                 else

                        return FALSE;

        }

        return TRUE;

}

  The two  internal  routines  that  do  most  of  the  message  unpacking  are

_Xget_random  and _Xget_secret. These routines respectively call get_random and

get_secret with the appropriate parameters. When those routines return, a reply

message is created in the buffer that OutHeadP points to. 

  The following is the code for _Xget_random;

/* Routine get_random */

mig_internal novalue _Xget_random(InHeadP, OutHeadP)

        msg_header_t *InHeadP, *OutHeadP;

{

        typedef struct {

                msg_header_t Head;

        } Request; 

        typedef struct {

                msg_header_t Head;

                msg_type_t RetCodeType;

                kern_return_t RetCode;

                msg_type_t numType;

                int num;

        } Reply; 

        register Request *InP = (Request *) InHeadP;

        register Reply *OutP = (Reply *) OutHeadP;

        extern kern_return_t get_random(); 

#if     UseStaticMsgType

        static msg_type_t numType = {

                /* msg_type_name = */           MSG_TYPE_INTEGER_32,

                /* msg_type_size = */           32,

                /* msg_type_number = */         1,

                /* msg_type_inline = */         TRUE,

                /* msg_type_longform = */       FALSE,

                /* msg_type_deallocate = */     FALSE,

        };

#endif  UseStaticMsgType 

#if     TypeCheck

        if (InP->Head.msg_size != sizeof(Request))

                { OutP->RetCode = MIG_BAD_ARGUMENTS; return; }

#endif  TypeCheck 

        OutP->RetCode = get_random(InP->Head.msg_local_port,

                                        &OutP->num);

        if (OutP->RetCode != KERN_SUCCESS)

                return;

        OutP->Head.msg_simple = TRUE;

        OutP->Head.msg_size = sizeof(Reply); 

#if     UseStaticMsgType

        OutP->numType = numType;

#else   UseStaticMsgType

        OutP->numType.msg_type_name = MSG_TYPE_INTEGER_32;

        OutP->numType.msg_type_size = 32;

        OutP->numType.msg_type_number = 1;

        OutP->numType.msg_type_inline = TRUE;

        OutP->numType.msg_type_longform = FALSE;

        OutP->numType.msg_type_deallocate = FALSE;

#endif  UseStaticMsgType 


 
 

3.4. Server procedures

  Finally  the  subsystem  implementor must write the proceedures that actually

perform the requested operations. For this server the following code will do.

/* procedues of random_server */

#include <mach.h>

#include "random_types.h" 

long    random(); 
 

kern_return_t get_random(serv_port,num)

/* get_random returns a random number between 0 and 2**32 - 1 */

    port_t      serv_port;

    int         *num;

{

        *num = (int) random();

        return(KERN_SUCCESS);


kern_return_t get_secret(serv_port,password)

    port_t      serv_port;

    string25    password;

/* get_secret returns a random printable ascii string 25 chars long */

{

    int         i,j;

    int         range = (int)'~' - (int)' '; 

        for (i=0;i<25; i++)

        {

                j = ((int)random()%range) + (int)' ';

                password[i] = (char)j;

        }

        password[25] = '\0';

        return(KERN_SUCCESS);


 
 

3.5. User side

  The following code shows an example of how to use the server.

/* This is an example of a program using the random server */

#include <mach.h>

#include <servers/netname.h>

#include "random.h"

#include <mach_error.h>

#include <stdio.h> 
 

main()

{

    int         number;

    string25    password;

    port_t      serv_port;

    kern_return_t retcode; 

        printf("Looking up port for random server\n");

        retcode = netname_look_up(name_server_port,"","RandomServerPort",

                        &serv_port);

        if (retcode != KERN_SUCCESS)

        {    mach_error("error in looking up port for random server",retcode);

             printf("Random server may not be running\n");

             exit();

        }

        printf("Calling get_random\n");

        retcode = get_random(serv_port,&number);

        if (retcode == KERN_SUCCESS)

                printf("Result from get_random is %d\n",number);

        else mach_error("Error from get_random is",retcode);

        printf("Calling get_secret\n");

        retcode = get_secret(serv_port,password);

        if (retcode == KERN_SUCCESS)

                printf("Result from get_secret is %s\n",password);

        else mach_error("Error from get_secret is",retcode);


4. General Mach information

  Structure of the Mach tree

At CMU the Mach tree may either be used from /afs or  may  be  supped  to  your

workstation.  If  you  wish to have a local copy supped to your workstation use

the command.

    modmisc + cs.misc.mach [-release beta|default] [-config minimal] 

  Directories of interest are:

        /usr/mach/man : Mach manual pages

        /usr/mach/bin : Mach system programs such as MiG

        /usr/mach/doc : on line copies of Mach documents

        /usr/mach/etc : Mach servers such as the environment manager

        /usr/mach/include : Mach include files

        /usr/mach/lib : Mach libraries such as libmach.a and libthreads.a 
 

  Where to find examples and manuals

The Mach sources are kept in /afs/cs/project/mach/src.  There  is  subdirectory

examples  in  /usr/mach/doc that contains the example programs that are used in

the two tutorial documents. 

  Hard copies of the Mach manuals and documents can be found in WeH 7114.    To

print  a copy yourself from the /usr/mach/doc/ directory, use the .ps files for

the laser writers. See the lpr UNIX manual entry on how to print .ps files. 

  Setting up search paths

To compile or load a Mach program your paths must be set to look in the  proper

place   for   Mach   include   files   and   libraries.      By  typing  source

/usr/mach/lib/machpaths the appropriate Mach directories will be added to  your

existing paths. 

  Mach libraries

The  Mach  library,  -lmach,  must be loaded with most of the examples given in

this tutorial.  Any program using  Mach  kernel  interface  functions  must  be

loaded with libmach and the /usr/cs/lib version of crt0.  For example, assuming

that /usr/mach/lib and /usr/cs/lib are on your LPATH:

        cp /../wb1/usr/mach/src/examples/simp_ipc.c .

        cc -o simp_ipc simp_ipc.c -lmach 
 

  Two threads libraries exist: libthreads.a  and  libco_threads.a.    The  'co'

(co-routine version of this library does not actually use Mach threads; it uses

coroutines.  This library does not provide  real  parallelism,  a  single  UNIX

process  is  running.    The  other  threads  library,  libthreads.a, uses Mach

threads. 

  Mach include files

When writing a program that uses Mach facilities some of the following  include

files may be needed:

        mach.h : needed for all Mach programs

        mach/message.h : if any message structures are used

        mach_error.h : if mach_error() is used

        servers/env_mgr.h : if environment manager server is used

        servers/netname.h : if netname server is used

        cthreads.h : if C threads package is used 
 

  Mach information/questions

A  Mach  bboard  exists  to  keep  the  user  community  up to date on new Mach

releases.  This bboard is called 'mach'.  See UNIX bboard manual entry. 

  If you have questions relating to the use of  any  Mach  facilities,  or  any

comments  on  this  tutorial,  send mail to machlib.  Comments on this tutorial

will be greatly appreciated.

                               Table of Contents

1. Introduction                                                               1

2. C Threads, A Single Master Thread Spawning Concurrent Slaves               1

     2.1. Initializing the C Threads Package                                  1

     2.2. Allocation of a Mutex Variable                                      1

     2.3. Locking a Mutex Variable                                            1

     2.4. Unlocking a Mutex Variable                                          1

     2.5. Allocation of a Condition Variable                                  1

     2.6. Waiting on a Condition                                              1

     2.7. Signalling a Condition                                              1

     2.8. Forking a C Thread                                                  2

     2.9. Detaching a C Thread                                                2

     2.10. Yielding the Processor to other Threads                            2

     2.11. Exiting a C Thread                                                 2

     2.12. Example V, masterslave.c                                           3

3. MIG - The Mach Interface Generator                                         4

     3.1. MIG Definition file                                                 4

     3.2. Server main program                                                 4

     3.3. Server message dispatch code                                        5

     3.4. Server procedures                                                   6

     3.5. User side                                                           6

4. General Mach information                                                   6


Set Home | Add to Favorites

All Rights Reserved Powered by Free Document Search and Download

Copyright © 2011
This site does not host pdf,doc,ppt,xls,rtf,txt files all document are the property of their respective owners. complaint#nuokui.com
TOP