home *** CD-ROM | disk | FTP | other *** search
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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
-