home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / mach / doc / unpublished / machsys.doc.Z / machsys.doc
Encoding:
Text File  |  1992-09-01  |  50.9 KB  |  1,209 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.                  A PROGRAMMER'S GUIDE TO THE MACH SYSTEM CALLS
  16.  
  17.                                 Linda R. Walmer
  18.                                Mary R. Thompson
  19.  
  20.                         Department of Computer Science
  21.                           Carnegie-Mellon University
  22.                              Pittsburgh, PA 15213
  23.                                   Version of:
  24.                                16 November 1989
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.                                    ABSTRACT
  39.  
  40. This  document is one of two tutorials designed to teach basic Mach programming
  41. skills.  This manual explains the use of the Mach kernel calls.  The  companion
  42. document  to  this  one,  A  Programmer's  Guide  to  the Mach User Environment
  43. explains the use  of  higher  level  methods  for  implementing  multi-threaded
  44. programs  and interprocess communication.  Before writing programs that use the
  45. system calls directly, the user should be aware that the  methods  outlined  in
  46. the other document may be used to solve his problem more simply.
  47.  
  48. Comments, suggestions and additions to this document are wlecome.
  49. 1. Introduction
  50.  
  51.  
  52.  
  53. 1.1. Tutorial documents
  54.   This  document  is  one  of  two  tutorials  designed  to  teach  basic  Mach
  55. programming skills.  This manual explains the use of the Mach kernel calls.  It
  56. begins  with an introduction to the basic Mach abstractions of ports, messages,
  57. virtual memory, tasks and threads. It then contains a number of simple programs
  58. which send and receive Mach messages, and use virtual memory.
  59.  
  60.   There  is  a companion document to this one, A Programmer's Guide to the Mach
  61. User Environment that explains the use of higher level methods for implementing
  62. multi-threaded   programs  and  interprocess  communication.    Before  writing
  63. programs that use the system calls directly, the user should be aware that  the
  64. methods  outlined  in  the other document may be used to solve his problem more
  65. simply.
  66.  
  67.   The final section of A  Programmer's  Guide  to  the  Mach  User  Environment
  68. describes where to find the mach environment on-line at CMU and how to use it.
  69.  
  70.  
  71.  
  72. 1.2. Basic Mach Concepts
  73.   In  many  ways the Mach operating system can be viewed as an extension of the
  74. UNIX operating system.  Existing 4.3bsd programs which  do  not  use  knowledge
  75. about  internal  UNIX  data  structures  will  continue  to  function  in Mach.
  76. However, Mach provides a number of new features not  available  in  traditional
  77. UNIX systems.  The primary motivation for the differences between Mach and UNIX
  78. was a  desire  to  better  support  multiprocessors  and  to  provide  a  solid
  79. foundation for distributed computing.
  80.  
  81.   In order to use Mach's new features, the programmer needs to be familiar with
  82. four fundamental Mach abstractions:
  83.  
  84.    - A task is an execution environment, including a paged virtual address
  85.      space and protected access to system resources such as processors and
  86.      ports. In general for a task to be useful, it must have at least  one
  87.      thread  executing within it. Thus when we speak of communicating with
  88.      a task, it means to communicate with a thread running in that task. A
  89.      task with one thread is the Mach equivalent of a traditional process.
  90.  
  91.    - A  thread  is the basic unit of execution. It consists of a processor
  92.      state, an execution stack and a limited amount of per  thread  static
  93.      storage.  It shares all other memory and resources with all the other
  94.      threads executing in the same task. A thread can only execute in  one
  95.      task.
  96.  
  97.    - A  port  is  a  communication  channel  - a logical queue of messages
  98.      protected by the kernel. Only one task can receive  messages  from  a
  99.      port, but all tasks that have access to the port can send messages.
  100.  
  101.    - A message is a typed collection of data objects used in communication
  102.      between threads.
  103.  
  104.   This tutorial presents and explains several simple programs which make use of
  105. these  Mach abstractions to solve simple programming problems.  A more detailed
  106. explanation of the basic Mach abstractions can be  found  in  the  Unix  Review
  107. article  Threads  of  a New System, Richard F. Rashid, Aug. 1986 or in the Mach
  108. Kernel Interface Manual.   The  latter  document  gives  the  complete  calling
  109. semantics for all the Mach system calls.
  110.  
  111.   Tasks  versus  threads.    Mach  tasks  have  independent  address spaces and
  112. typically communicate by sending messages to each other.  Separate tasks can be
  113. used to perform parts of a computation on different workstations connected by a
  114. network.  The port and message passing facilities of Mach have been designed to
  115. allow transparent communication between tasks whether they are on the same node
  116. or on two separate nodes in a network.  All  message  operations  are  location
  117. independent and, in theory, it is impossible to tell whether a message has been
  118. sent to or received from a task on  the  same  machine  or  a  remote  one.  In
  119. practice,  however,  the  timing  and failure modes are different between local
  120. messages and remote messages.  System services such as remote file  access  and
  121. network message communication are themselves implemented as tasks communicating
  122. via messages.
  123.  
  124.   Threads, on the other hand, share their memory and  access  rights  with  the
  125. other  threads  in a task.  They often communicate within a task through shared
  126. memory locations.  Threads are intended to allow separate  execution  units  to
  127. work  in  parallel  on  the  same problem. This gives a user an easy way to get
  128. parallel computation on a multi-processor.  On  a  single  processor,  multiple
  129. threads may simplify the structure of a program that is logically doing several
  130. different functions. Multiple threads are also useful if some  of  a  program's
  131. actions  may  cause  a  line  of  exection  to be blocked, while other lines of
  132. execution could usefully continue.  For example, server that  handles  requests
  133. from  multiple  clients  may  find one request blocked, but be able to continue
  134. working on another request.  Creating or destroying a thread  is  also  a  much
  135. less expensive operation than creating or destroying a task.
  136.  
  137.   Communications.    There  are  two basic ways to communicate between tasks or
  138. between threads within a task: shared memory and message passing  (IPC).    The
  139. most  obvious  and  probably  most  efficient form of communication between two
  140. threads in the same task is through shared memory.  The  most  common  form  of
  141. communication between tasks is through message passing. However, threads in the
  142. same task may send messages to each other as long as the programmer is  careful
  143. about  which  threads  are  waiting  for  messages  on which ports. Also, it is
  144. possible for a task to share memory with tasks that  have  a  common  ancestor.
  145. Since  these  tasks  will  probably  be on the same machine this sharing can be
  146. efficient. Unrelated tasks can also share memory,  but  that  style  of  memory
  147. sharing  is  made  potentially  more  complex  when two unrelated tasks are not
  148. located on the same node.   Memory  sharing  between  unrelated  tasks  is  not
  149. covered  in  this  tutorial.  When two threads/tasks are using the same memory,
  150. locking is often needed. Unfortunately, hardware mechanisms for locking  memory
  151. locations vary from one machine to another.  The Mach C threads library package
  152. provides machine-independent locking primitives. Tasks that  don't  use  the  C
  153. threads library must provide their own locking protocols.
  154.  
  155.   Virtual  memory  primitives versus malloc.  The Mach kernel provides a set of
  156. primitives to allow a programmer to manipulate the virtual address space  of  a
  157. task.  The  two most fundamental ones are vm_allocate to get new virtual memory
  158. and vm_deallocate to free virtual memory.  The programmer  also  has  available
  159. the UNIX functions sbrk, malloc and calloc.
  160.  
  161.   The decision to use one allocation method rather than another should be based
  162. on several factors. sbrk  is  now  obsolete  and  only  retained  for  backward
  163. compatibility  with  older  UNIX  programs.    It  is  not recommended that new
  164. programs which expect to use Mach features should use  sbrk.    In  fact,  sbrk
  165. calls vm_allocate to increase the user's address space. vm_allocate always adds
  166. new, zero-filled virtual memory in paged-aligned, multiple of page-sized chunks
  167. (where  a page is currenly 4K or 8K bytes ). Malloc allocates approximately the
  168. size it is asked for (plus a few bytes) out of a pre-allocated heap. calloc  is
  169. the  same as malloc except that it zeros the memory before returning it. malloc
  170. and calloc are library subroutine calls while vm_allocate is a  kernel  syscall
  171. which is somewhat more expensive.
  172.  
  173.   The  the  most obvious basis on which to choose an allocation function is the
  174. size of the desired space.  There is one other consideration, however, which is
  175. the desirability of page-aligned storage. If the memory that is allocated is to
  176. be passed out-of-line in a message, it is more efficient if it is page-aligned.
  177. Note  that  it  is essential that the correct deallocation function be used. If
  178. memory has been vm_allocated it must be vm_deallocated, if it was  malloced  it
  179. must  be  freed.    Memory that is received out-of-line from a message has been
  180. vm_allocated by the kernel.
  181.  
  182.   Standard Mach servers.  There are a couple of standard servers  that  support
  183. use  of  Mach style communications. One is the NetMsgServer. It passes Mach IPC
  184. messages between machines. It also provides network-wide port registration  and
  185. lookup   functions.   The   names  of  these  calls  are  netname_check_in  and
  186. netname_look_up and are documented in the man section  netname.3.    The  other
  187. general  purpose  Mach  server  is  the Environment Manager. It can register or
  188. lookup ports or named strings but does not communicate with  other  Environment
  189. Managers.  The functions that it provides are documented in the manual The Mach
  190. Environment  Manager  or  in  the  man  sections  env_conn.3,  env_list.3,  and
  191. env_port.3. In general, one decides to register a port with the NetMsgServer if
  192. it is to be known by tasks  on  arbitrary  remote  machines  within  the  local
  193. network.    Ports are registered with the Environment Manager if they are to be
  194. used only by tasks which share access to the same Environment Manager.    Often
  195. such  tasks  are part of the same creation tree or are performing a computation
  196. on a single node.
  197.  
  198.   The examples in this document demonstrate the creation of tasks and  threads,
  199. message  passing  between  tasks, shared memory communication between tasks and
  200. threads, and the use of the virtual memory primitives.
  201.  
  202. 2. Message Communication Between Processes
  203.   The first sample program shows how to pass messages between two  tasks.  This
  204. is a good illustration of the following fundamental Mach features:  allocation,
  205. deallocation, and use of ports; use of  the  Environment  Manager;  setting  up
  206. message  structures;  and  communication  between two processes via messages on
  207. ports.  In this example the parent task will fork a child task, which will send
  208. the  parent  a  message containing data.  The parent will then notify the child
  209. that he received the data by sending a reply message.
  210.  
  211.   At this point, the reader should be aware that most MACH programmers  do  not
  212. code  IPC at this level of detail, but instead use the Mach Interface Generator
  213. (MIG) to produce the message handling code.  The use of MIG is explained in the
  214. Programmers's  Guide  to  the  Mach User Environment. Users who are new the the
  215. MACH probably want to read  that  document  before  attempting  to  write  code
  216. following these examples.
  217.  
  218.   This  example  uses a Mach version of the UNIX fork utility to create a child
  219. task. The UNIX part of the fork creates a complete copy of the parent's address
  220. space  and  prepares  the  child  to begin executing immediately after the fork
  221. call. The Mach part of the fork creates two ports for the child task: its  task
  222. kernel  port,  defined  by  task_self();  and  a  notification port, defined by
  223. task_notify().  The task port is the port that represents that task in calls to
  224. the  kernel.  The notify port is the port on which the task may receive special
  225. messages from the kernel.  The child task also inherits an  exception  port,  a
  226. bootstrap  port  and  some  ports  for  system  servers such as the Environment
  227. Manager and the Netmsgserver. Access to user defined  ports  is  not  inherited
  228. through  forking.    The  thread  that  is  created  has  a thread kernel port,
  229. referenced  by  thread_self(),  and  a  thread  reply   port,   referenced   by
  230. thread_reply()  created  for  it.    The  thread  kernel  port is the port that
  231. represents the thread in kernel call. The thread reply port is a port on  which
  232. the thread can receive any initialization messages from its parent.
  233.  
  234.   Message  passing  between the parent and child cannot take place until a port
  235. is known by both processes.  Before forking, a string is constructed to be used
  236. as  the  name  of  the  communication  port  and  a port is allocated using the
  237. port_allocate call. Then  the  Environment  Manager  function  env_set_port  is
  238. called  to  associate  the  name with the port.  This name is available to both
  239. processes after forking since it is a static variable.    After  the  fork  the
  240. child can acquire send rights to the port using env_get_port.
  241.  
  242.   Now  that  both  tasks  have  access  to the communication port, a message is
  243. constructed by the child.  This message contains a fixed sized  message  header
  244. and  a  variable  sized data portion.  When constructing the message, the child
  245. sets the  msg_remote_port  field  in  the  header  to  the  communication  port
  246. established earlier.  This field designates the port to which the message is to
  247. be sent.  Another header field that the child must be sure to set  properly  is
  248. msg_local_port.    This  is  the  port on which the child will wait for a reply
  249. message.  In this example, the child will receive  the  reply  message  on  his
  250. thread  reply port. The task that receives the message constructed by the child
  251. automatically receives send rights to the msg_local_port.  Since the child task
  252. wishes  to send a message and then immediately receive a reply message, it uses
  253. msg_rpc instead of msg_send and msg_receive.  msg_rpc does a send followed by a
  254. receive using the same message buffer for both calls.
  255.  
  256.   Next  the  parent  takes  a  message structure and fills in the header fields
  257. needed by msg_receive: msg_remote_port, representing  the  port  on  which  the
  258. message  is  to  be  received,  and  msg_size, the maximum size of the expected
  259. message.    With  this  message  structure,  the  parent   calls   msg_receive.
  260. msg_receive   returns  the  message  queued  on  the  port  designated  by  the
  261. msg_remote_port field.  Remember that the child's message contained a  port  to
  262. send  a  reply  message  to,  the  msg_local_port  field.    Upon  return  from
  263. msg_receive, the msg_remote_port field is set  to  the  child's  msg_local_port
  264. field, the expected reply port.
  265.  
  266.   In  our  example,  the  parent  is  going to send a message back to the child
  267. indicating that it received the  message  containing  the  data.    This  reply
  268. message  contains  no  data itself; it is just a confirmation.  The parent sets
  269. the msg_remote_port field of the reply message to the msg_remote_port field  of
  270. the  previously  received  message.    msg_send is now called to send the reply
  271. message to the port indicated by the msg_remote_port field.
  272.  
  273.   The earlier call of msg_rpc by the child will now receive the parent's  reply
  274. message.  Our example is over except for cleaning up. env_del_port is called to
  275. let the Environment Manager know the name/port association is no longer needed.
  276. port_deallocate  is then called by the parent which owns the communication port
  277. to destroy it.
  278.  
  279.   Detailed discussion of the various calls used by the example are  given  next
  280. and the complete text of the program is given in Section 2.12.
  281.  
  282.  
  283.  
  284. 2.1. Mach Error Printing
  285.   mach_error is an error routine that accepts a string and an error value.  The
  286. string is then printed along with an error string associated with the value.
  287.  
  288.     kern_return_t   error;
  289.  
  290.     mach_error("PARENT: port_allocate returned value of ", error);
  291.  
  292.  
  293.  
  294.  
  295. 2.2. Port Allocation
  296.   port_allocate is used to create a port.  The first argument to  port_allocate
  297. is  the  task  the  port  is  to  belong to, in this case the process itself or
  298. task_self().
  299.  
  300.     port_t          result;
  301.     kern_return_t   error;
  302.  
  303.     if ((error = port_allocate(task_self(), &result)) != KERN_SUCCESS) {
  304.             mach_error("PARENT: port_allocate returned value of ", error);
  305.             exit(1);
  306.     }
  307.  
  308.  
  309.  
  310.  
  311. 2.3. Port Deallocation
  312.   port_deallocate is used to relinquish a task's access to a port. If the  task
  313. has  ownership and receive rights to the port, the port_deallocate destroys the
  314. port and notifies (on their notify ports) all the other tasks  that  have  send
  315. rights to the port.
  316.  
  317.     port_t          my_port;
  318.     kern_return_t   error;
  319.  
  320.     if ((error = port_deallocate(task_self(), my_port)) != KERN_SUCCESS) {
  321.           mach_error("PARENT: port_deallocate returned value of ", error);
  322.           exit(1);
  323.     }
  324.  
  325.  
  326.  
  327.  
  328. 2.4. Environment Manager Server/Checking in a Port
  329.   The   Environment   Manager   is  used  as  a  repository  for  named  ports.
  330. env_get_port can be used to associate a name with a port.  Note that  the  port
  331. must   have  been  previously  acquired  either  through  a  message,  or  from
  332. port_allocate, or be one of the special system ports that are aquired  on  task
  333. creation. Name has been set to a string.
  334.  
  335.     env_name_t      name;
  336.     port_t          comm_port;
  337.     kern_return_t   error;
  338.  
  339.  
  340.     if ((error = env_set_port(environment_port, name,
  341.                               comm_port)) != KERN_SUCCESS) {
  342.             mach_error("PARENT: env_set_port returned value of ", error);
  343.             exit(1);
  344.     }
  345.  
  346.  
  347.  
  348.  
  349. 2.5. Environment Manager Server/Looking up a Port
  350.   env_get_port  can  be  used  to  look  up a port when the name of the port is
  351. known.  If env_set_port has not been called to associate a port with the  given
  352. name, env_get_port will fail.
  353.  
  354.     env_name_t      name;
  355.     port_t          comm_port;
  356.     kern_return_t   error;
  357.  
  358.     /* Name has been previously set to a desired string. */
  359.  
  360.     if ((error = env_get_port(environment_port, name,
  361.                             &comm_port)) != KERN_SUCCESS) {
  362.             mach_error("env_get_port returned ", error);
  363.             exit(1);
  364.     }
  365.  
  366.  
  367.  
  368.  
  369. 2.6. Setting up a Simple Message
  370.   A  message  consists  of  a  fixed  length  header  defined  by the structure
  371. msg_header_t followed by a variable number of typed data items.  A  message  is
  372. simple  if  it  does  not contain any out-of-line data (pointers) or any ports.
  373. The msg_remote_port field must contain the port to which the message is  to  be
  374. sent.  In this case it is comm_port.  The msg_local_port field should be set to
  375. the port on which a reply message is expected.  thread_reply(),  which  returns
  376. the thread's reply port is used as the reply port.
  377.  
  378.     typedef struct {
  379.                     unsigned int    :24,
  380.                                     msg_simple : 8;
  381.                     unsigned int    msg_size;       /* in bytes */
  382.                     int             msg_type;       /* NORMAL,EMERGENCY*/
  383.                     port_t          msg_local_port;
  384.                     port_t          msg_remote_port;
  385.                     int             msg_id;         /* user supplied id */
  386.     } msg_header_t;
  387.  
  388.  
  389.     struct simp_msg_struct {
  390.             msg_header_t    h;
  391.             msg_type_t      t;
  392.             int             inline_data[MAXDATA];
  393.     };
  394.     struct simp_msg_struct  msg_xmt;
  395.     port_t          comm_port;
  396.  
  397.  
  398.     msg_xmt= &msg_xmt_data;
  399.     msg_xmt.h.msg_local_port = thread_reply();
  400.     msg_xmt.h.msg_remote_port = comm_port;
  401.     msg_xmt.h.msg_size = sizeof(struct simp_msg_struct);
  402.     msg_xmt.h.msg_id = 0x12345678;
  403.     msg_xmt.h.msg_type = MSG_TYPE_NORMAL;
  404.     msg_xmt.h.msg_simple = TRUE;
  405.  
  406.     msg_xmt.t.msg_type_name = MSG_TYPE_INTEGER_32;
  407.     msg_xmt.t.msg_type_size = 32;
  408.     msg_xmt.t.msg_type_number = MAXDATA;
  409.     msg_xmt.t.msg_type_inline = TRUE;
  410.     msg_xmt.t.msg_type_longform = FALSE;
  411.     msg_xmt.t.msg_type_deallocate = FALSE;
  412.     /* fill the msg_xmt.inline_data array with the desired data*/
  413.  
  414.  
  415.  
  416.  
  417. 2.7. Sending Messages
  418.   The  first  parameter  to  msg_send  is  the address of a msg_header_t.  This
  419. message will be sent to the port indicated by the msg_remote_port field.   Send
  420. rights  to msg_local_port are given to the receiver so that it may send a reply
  421. message.
  422.  
  423.     msg_return_t    ret;
  424.     struct simp_msg_struct {
  425.             msg_header_t    h;
  426.             msg_type_t      t;
  427.             int             inline_data[MAXDATA];
  428.     };
  429.     struct simp_msg_struct  msg_xmt;
  430.  
  431.     if ((ret = msg_send(&msg_xmt.h, MSG_OPTION_NONE, 0)) != SEND_SUCCESS){
  432.             mach_error("CHILD: msg_send returned value of ", ret);
  433.             exit(1);
  434.     }
  435.  
  436.  
  437.  
  438.  
  439. 2.8. Receiving Messages
  440.   msg_receive is used to retrieve the next message from a port specified in the
  441. msg_remote_port  field.    The  field  msg_size  must be set to the size of the
  442. buffer for the message and thus the maximum permitted size of the message being
  443. received.    If  the message that is queued on the port is too big, the receive
  444. will fail. When msg_receive returns, the msg_remote_port field will be  set  to
  445. the sender's msg_local_port field, or the port that reply messages are expected
  446. on and msg_size will be set to the size of the message that was received.
  447.  
  448.     msg_return_t    ret;
  449.     struct simp_msg_struct {
  450.             msg_header_t    h;
  451.             msg_type_t      t;
  452.             int             inline_data[MAXDATA];
  453.     };
  454.     struct simp_msg_struct  msg_rcv;
  455.  
  456.     msg_rcv.h.msg_size = sizeof(msg_rcv);
  457.     msg_rcv.h.msg_local_port = comm_port;
  458.     if ((ret = msg_receive(&msg_rcv.h, MSG_OPTION_NONE, 0)) !=
  459.             RCV_SUCCESS){
  460.             mach_error("CHILD: msg_receive returned value of ", ret);
  461.             exit(1);
  462.     }
  463.  
  464.  
  465.  
  466.  
  467. 2.9. Setting up a Reply Message
  468.   At this point a message has already been received in the  structure  msg_rcv.
  469. A  reply  message is to be constructed and sent to the sender of msg_rcv.  Note
  470. that the reply message, msg_xmt is simply  a  msg_header_t  since  no  data  is
  471. required.  The  msg_remote_port field, where to send the message, is set to the
  472. remote port of the previously received message.  The earlier  msg_receive  call
  473. set  the  remote port field of msg_rcv to the msg_local_port field specified by
  474. the sender. See the comment in Section 2.6 about setting up the  msg_local_port
  475. field.
  476.  
  477.     struct simp_msg_struct {
  478.             msg_header_t    h;
  479.             msg_type_t      t;
  480.             int             inline_data[MAXDATA];
  481.     };
  482.     msg_header_t            msg_xmt;
  483.     struct simp_msg_struct  *msg_rcv;
  484.  
  485.  
  486.     msg_xmt.h.msg_remote_port = msg_rcv->h.msg_remote_port;
  487.     msg_xmt.h.msg_local_port = PORT_NULL; /* no reply expected */
  488.     msg_xmt.h.msg_id = 0x12345678;
  489.     msg_xmt.h.msg_size = sizeof(msg_header_t);
  490.     msg_xmt.h.msg_type = MSG_TYPE_NORMAL;
  491.     msg_xmt.h.msg_simple = TRUE;
  492.  
  493.  
  494.  
  495.  
  496.  
  497. 2.10. RPC, Send/Receive
  498.   msg_rpc  does  a  msg_send  followed  by a msg_receive using the same message
  499. buffer.  msg_size is, as usual, the size of the message  that  is  being  sent.
  500. The  third  parameter  to  msg_rpc  represents  the maximum size of the message
  501. buffer for the message to be received. In this case  it  is  the  size  of  the
  502. message being sent because we know that the reply message is smaller.
  503.  
  504.     msg_return_t    ret;
  505.     struct simp_msg_struct {
  506.             msg_header_t    h;
  507.             msg_type_t      t;
  508.             int             inline_data[MAXDATA];
  509.     };
  510.     struct simp_msg_struct  msg_xmt;
  511.  
  512.     if ((ret = msg_rpc(&msg_xmt.h, MSG_OPTION_NONE, sizeof(msg_xmt), 0,
  513.             0)) != RPC_SUCCESS) {
  514.             mach_error("CHILD: msg_rpc returned value of ", ret);
  515.             exit(1);
  516.     }
  517.  
  518.  
  519.  
  520.  
  521. 2.11. A Non-Simple Message
  522.   Messages  are non-simple if they contain ports or out-of-line data.  The next
  523. example shows how to construct a data containing  out-of-line  data.  The  most
  524. common  reasons  for  sending  data out-of-line are that the data block is very
  525. large or is of variable size. In-line data is copied by  the  sender  into  the
  526. message  structure  and  then  often copied out of the message by the receiver.
  527. Out-of-line data, however, is mapped by the kernel from the  address  space  of
  528. the  sender  to the address space of the receiver. No actual copying of data is
  529. done unless one of the two tasks subsequently modifies the  data.  This  is  an
  530. example of copy-on-write data sharing.
  531.  
  532.   The  fields  that  change values from those in the simple message example are
  533. msg_simple, msg_type_inline, and possibly msg_type_deallocate.  See Section 2.6
  534. for  details  on  msg_remote_port  and  msg_local_port.   An example program of
  535. non-simple message passing can be found in the Mach  examples  directory.  This
  536. example  is  not  included  in  this  document,  but  can  be found in the file
  537. ool_ipc.c in the Mach examples directory.
  538.  
  539.     struct ool_msg_struct {
  540.             msg_header_t    h;
  541.             msg_type_t      t;
  542.             int             *out_of_line_data;
  543.     };
  544.     struct ool_msg_struct   msg_xmt;
  545.     port_t          comm_port;
  546.  
  547.  
  548.     msg_xmt.h.msg_local_port = thread_reply();
  549.     msg_xmt.h.msg_remote_port = comm_port;
  550.     msg_xmt.h.msg_size = sizeof(struct ool_msg_struct);
  551.     msg_xmt.h.msg_id = 0x12345678;
  552.     msg_xmt.h.msg_type = MSG_TYPE_NORMAL;
  553.     msg_xmt.h.msg_simple = FALSE;
  554.  
  555.     msg_xmt.t.msg_type_name = MSG_TYPE_INTEGER_32;
  556.     msg_xmt.t.msg_type_size = 32;
  557.     msg_xmt.t.msg_type_number = MAXDATA;
  558.     msg_xmt.t.msg_type_inline = FALSE;
  559.     msg_xmt.t.msg_type_longform = FALSE;
  560.     msg_xmt.t.msg_type_deallocate = FALSE;
  561.     /* set msg_xmt.out_of_line_data to point to the data */
  562. 2.12. Programming Example I, simp_ipc.c
  563. /*
  564.  * This program is an illustration of MACH message passing from a child
  565.  * to the parent process and back.  In this example, the child is passing
  566.  * a simple message where the data is stored in the message. The program
  567.  * allocates a port to use for communication.  The environment manager
  568.  * is used to register the port with a name that both the parent
  569.  * and child know. The program forks a child process which
  570.  * then uses env_get_port to acquire the port needed for communication.
  571.  * A message, containing the data the parent needs, is formed by the child
  572.  * and sent with msg_rpc to the parent.  msg_rpc does a send and a receive
  573.  * using the same message buffer.  The parent does a receive on the
  574.  * established communication port receiving the message from the child.
  575.  * Upon receiving the child's message, the parent constructs and sends
  576.  * a confirmation or reply message back to the child indicating he received
  577.  * the child's message and data.  The call to msg_rpc by the child
  578.  * receives the parent's reply.  The child then tells the environment
  579.  * manager the communication port is no longer needed, and calls
  580.  * port_deallocate.
  581.  *
  582.  **********************************************************/
  583.  
  584. #include <stdio.h>
  585. #include <mach.h>
  586. #include <mach_error.h>
  587. #include <mach/message.h>
  588. #include <servers/env_mgr.h>
  589.  
  590. #define MAXDATA 20
  591.  
  592. /*  simple message structure */
  593. struct simp_msg_struct {
  594.         msg_header_t    h;
  595.         msg_type_t      t;
  596.         int             inline_data[MAXDATA];
  597. };
  598.  
  599.  
  600.  
  601. /* This routine is used to set up the message containing the data that
  602.  * the child will send to the parent.  Here the data is a simple array of
  603.  * integers.  */
  604.  
  605. void setup_simp_request(msg_xmt, my_port)
  606.         struct simp_msg_struct  *msg_xmt;
  607.         port_t          my_port;
  608.  
  609. {
  610.         int i;
  611.  
  612.         msg_xmt->h.msg_local_port = thread_reply();
  613.         msg_xmt->h.msg_remote_port = my_port;
  614.         msg_xmt->h.msg_size = sizeof(struct simp_msg_struct);
  615.         msg_xmt->h.msg_id = 0x12345678;
  616.         msg_xmt->h.msg_type = MSG_TYPE_NORMAL;
  617.         msg_xmt->h.msg_simple = TRUE;
  618.  
  619.         msg_xmt->t.msg_type_name = MSG_TYPE_INTEGER_32;
  620.         msg_xmt->t.msg_type_size = 32;
  621.         msg_xmt->t.msg_type_number = MAXDATA;
  622.         msg_xmt->t.msg_type_inline = TRUE;
  623.         msg_xmt->t.msg_type_longform = FALSE;
  624.         msg_xmt->t.msg_type_deallocate = FALSE;
  625.         for (i = 0; i < MAXDATA; i++)
  626.                 msg_xmt->inline_data[i] = i;
  627.  
  628. }
  629.  
  630.  
  631. /* This procedure is used to set up the reply message that the parent is
  632.  * sending to the child.  Note that the remote_port of the received message
  633.  * designates where the reply message will be sent. No data is sent in this
  634.  * message, so the size of the message is simply the size of the message
  635.  * header.  */
  636.  
  637. void setup_simp_reply(msg_xmt, msg_rcv)
  638.         msg_header_t            *msg_xmt;
  639.         struct simp_msg_struct  *msg_rcv;
  640.  
  641. {
  642.         msg_xmt->msg_remote_port = msg_rcv->h.msg_remote_port;
  643.         msg_xmt->msg_local_port = PORT_NULL;
  644.         msg_xmt->msg_id = 0x12345678;
  645.         msg_xmt->msg_size = sizeof(msg_header_t);
  646.         msg_xmt->msg_type = MSG_TYPE_NORMAL;
  647.         msg_xmt->msg_simple = TRUE;
  648. }
  649.  
  650.  
  651.  
  652. /* This procedure is used by the child to get the communication port.
  653.  * The child got the name as part of its inherited static variable space.
  654.  * Port rights, however, are not inherited across forks. env_get_port,
  655.  * a utility of the environment manager is called to return the port
  656.  * associated with the given name.
  657.  */
  658.  
  659. port_t LookFor(name)
  660.         env_name_t      name;
  661.  
  662. {
  663.         port_t          result;
  664.         kern_return_t   error;
  665.  
  666.         if ((error = env_get_port(environment_port, name,
  667.                 &result)) != KERN_SUCCESS) {
  668.                         mach_error("CHILD: env_get_port returned ",
  669.                                 error);
  670.                         exit(1);
  671.                 }
  672.  
  673.         printf("CHILD: Successful env_get_port.\n");
  674.         return(result);
  675. }
  676.  
  677.  
  678.  
  679.  
  680. /* This routine is used by the parent to create a port, and to associate the
  681.  * port name with the port via the environment manager.
  682.  * port_allocate is used to allocate a port, and then env_set_port is called
  683.  * passing the name of the port, and the newly allocated port.   */
  684.  
  685. port_t Register(name)
  686.         env_name_t              name;
  687.  
  688. {
  689.         port_t          result;
  690.         kern_return_t   error;
  691.  
  692.         if ((error = port_allocate(task_self(), &result)) != KERN_SUCCESS) {
  693.                 mach_error("PARENT: port_allocate returned value of ", error);
  694.                 exit(1);
  695.         }
  696.         if ((error = env_set_port(environment_port, name,
  697.                 result)) != KERN_SUCCESS) {
  698.                 mach_error("PARENT: env_set_port returned value of ", error);
  699.                 exit(1);
  700.         }
  701.         printf("PARENT: Successful env_set_port.\n");
  702.         return(result);
  703. }
  704.  
  705.  
  706.  
  707.  
  708. /* This routine is called to demonstrate the passing of a simple message.
  709.  * Please see program comment for order of events. */
  710.  
  711. void child_routine(my_port)
  712.         port_t          my_port;
  713.  
  714. {
  715.         msg_return_t    ret;
  716.         int             i;
  717.         struct simp_msg_struct  msg_xmt, msg_rcv;
  718.  
  719.         setup_simp_request(&msg_xmt, my_port);
  720.         if ((ret = msg_rpc(&msg_xmt.h, MSG_OPTION_NONE, sizeof(msg_xmt), 0,
  721.                 0)) != RPC_SUCCESS) {
  722.                 mach_error("CHILD: msg_rpc returned value of ", ret);
  723.                 exit(1);
  724.         }
  725.         printf("CHILD: Successful msg_rpc.\n");
  726. }
  727.  
  728.  
  729.  
  730.  
  731. void parent_routine(my_port)
  732.         port_t          my_port;
  733.  
  734. {
  735.         msg_return_t    ret;
  736.         int             i;
  737.         int             x;
  738.         msg_header_t    msg_xmt;
  739.         struct simp_msg_struct msg_rcv;
  740.  
  741.         msg_rcv.h.msg_local_port = my_port;
  742.         msg_rcv.h.msg_size = sizeof(msg_rcv);
  743.         if ((ret = msg_receive(&msg_rcv.h, MSG_OPTION_NONE, 0)) !=
  744.                 RCV_SUCCESS) {
  745.                 mach_error("PARENT: msg_receive returned value of ", ret);
  746.                 exit(1);
  747.         }
  748.         printf("PARENT: Successful msg_receive.\n");
  749.         printf("PARENT: Data..");
  750.         for (i = 0; i < MAXDATA; i++)
  751.                 printf("%d ", msg_rcv.inline_data[i]);
  752.         printf("\n");
  753.         setup_simp_reply(&msg_xmt, &msg_rcv);
  754.         if ((ret = msg_send(&msg_xmt, MSG_OPTION_NONE,
  755.                 0)) != SEND_SUCCESS){
  756.                 mach_error("PARENT: msg_send returned value of ", ret);
  757.                 exit(1);
  758.         }
  759.         printf("PARENT: Successful msg_send.\n");
  760. }
  761.  
  762.  
  763.  
  764. main (argc, argv)
  765.         int             argc;
  766.         char            **argv;
  767.  
  768. {
  769.         kern_return_t   err;
  770.         port_t          my_port;
  771.         env_name_t      port_name;
  772.         int             fret;
  773.  
  774.         if (argc > 1) {
  775.                 printf("no arguments to simp_ipc\n");
  776.                 exit(1);
  777.         }
  778.         /* create a port name that both the child and parent will know */
  779.         sprintf(port_name, "ipc_test_%d", getpid());
  780.  
  781.         /* create and register port for parent to receive on */
  782.  
  783.         if ((my_port = Register(port_name)) == PORT_NULL)
  784.                 exit(1);
  785.  
  786.         /* fork returns 0 if child, and the child's ID to the parent.  */
  787.         fret = fork();
  788.         if (fret == 0) { /* child process */
  789.                 if ((my_port = LookFor(port_name)) == PORT_NULL)
  790.                         exit(1);
  791.                 child_routine(my_port);
  792.                 printf("CHILD: Finished successfully.\n");
  793.         }
  794.         else if (fret >0) { /* parent process */
  795.                 parent_routine(my_port);
  796.                 if ((err = env_del_port(environment_port, port_name))
  797.                         != KERN_SUCCESS) {
  798.                         mach_error("PARENT: env_del_port returned ", err);
  799.                         exit(1);
  800.                 }
  801.                 if ((err = port_deallocate(task_self(), my_port))
  802.                         != KERN_SUCCESS) {
  803.                         mach_error("PARENT: port_deallocate returned ", err);
  804.                         exit(1);
  805.                 }
  806.                 printf("PARENT: Finished successfully.\n");
  807.         }
  808.         else  printf("Error from fork.\n");
  809. }
  810. 3. Use of Virtual Memory
  811.  
  812.  
  813.  
  814. 3.1. Allocation, Deallocation, and Reading
  815.   The program for this  section  will  demonstrate  vm_allocate,  vm_read,  and
  816. vm_deallocate.   The purpose of this example is to check vm_read to be sure the
  817. data was read correctly.
  818.  
  819.   The first step in solving this problem is to get a chunk of memory  and  fill
  820. it  with  data.    vm_allocate is used to get the virtual memory.  Data is then
  821. stored in it.  The next step is to call vm_read.  vm_read allows a task to read
  822. another task's virtual memory.  Passing the address of the previously allocated
  823. memory as a starting point, vm_read is called.  Note  that  our  example  is  a
  824. simplified  use  of  vm_read  since  a task is reading its own memory.  vm_read
  825. returns a newly allocated region  containg  the  data  read.    Note  that  the
  826. parameter  size,  which  is the number of bytes to be read, must be an integral
  827. number of pages.  vm_read can be checked by comparing the  data  received  with
  828. the  previously allocated chunk.  If both spaces contain the same data, vm_read
  829. worked correctly.  To  clean  up  before  ending  this  example  program,  both
  830. allocated  spaces  must be deallocated by calling vm_deallocate.  Note that the
  831. data returned by vm_read must be deallocated.
  832.  
  833.  
  834. 3.1.1. Virtual Memory Allocation
  835.   vm_allocate allocates a region of virtual memory, placing it in the specified
  836. task's  address  space.   The size parameter is the number of bytes to allocate
  837. which is rounded to an integeral number of virtual pages.  If last parameter is
  838. TRUE  the  kernel  allocates a region of memory at the next convenient location
  839. and returns the virtual address as the second parameter.  If the last parameter
  840. is  FALSE, the kernel allocates memory starting at the address specified by the
  841. second parameter. vm_page_size is a global constant defined via mach.h. A  page
  842. of newly allocated memory is zero-filled when it is first touched.
  843.  
  844.     char            *data1;
  845.     kern_return_t   rtn;
  846.  
  847.     if ((rtn = vm_allocate(task_self(), &data1, vm_page_size,
  848.             TRUE)) != KERN_SUCCESS) {
  849.             mach_error("vm_allocate returned value of ", rtn);
  850.             printf("vmread: Exiting.\n");
  851.             exit(1);
  852.     }
  853.  
  854.  
  855.  
  856. 3.1.2. Virtual Memory Deallocation
  857.   vm_deallocate  affects  only  the  memory  of  task  specified  as  the first
  858. parameter.  This function reliquished access to the  memory  specified  in  the
  859. parameters:   address and size.  Other tasks which have access to this physical
  860. memory may continue to use it.  Note the size  is  expected  in  bytes  and  is
  861. rounded up to give a page boundary.  Never use vm_deallocate on memory that has
  862. been acquired by UNIX malloc.
  863.  
  864.     char            *data1;
  865.     kern_return_t   rtn;
  866.  
  867.     if ((rtn = vm_deallocate(task_self(), data1,
  868.             vm_page_size)) != KERN_SUCCESS) {
  869.             mach_error("vm_deallocate returned value of ", rtn);
  870.             printf("vmread: Exiting.\n");
  871.             exit(1);
  872.     }
  873.  
  874.  
  875.  
  876. 3.1.3. Virtual Memory Reading
  877.   vm_read makes it possible for one task to read the virtual memory of another.
  878. In the example below, a task is reading its own memory.  The first parameter to
  879. vm_read is the task whose address space is to be  read.    Note  the  parameter
  880. address,  which  is  the  first address to be read, must be on a page boundary.
  881. Size is in bytes and must be an integral number of pages.   The  data  read  is
  882. returned  in a newly allocated region.  The size in bytes of this new region is
  883. also returned.  vm_deallocate should be used on the region returned by  vm_read
  884. when it is no longer needed.
  885.  
  886.     char            *data1, *data2;
  887.     int             data_cnt;
  888.     kern_return_t   rtn;
  889.  
  890.     if ((rtn = vm_read(task_self(), data1, vm_page_size, &data2,
  891.             &data_cnt)) != KERN_SUCCESS) {
  892.             mach_error("vm_read returned value of ", rtn);
  893.             printf("vmread: Exiting.\n");
  894.             exit(1);
  895.     }
  896. 3.1.4. Programming Example II, vm_read.c
  897.  /*
  898.  *
  899.  * This program is a test of vm_allocate, vm_read and vm_deallocate.
  900.  * First some memory is allocated, and filled with data.  vm_read is
  901.  * then called, with reading starting at the previously allocated chunk.
  902.  * The contents of the two pieces of memory, one retreived by vm_allocate, and
  903.  * one by vm_read is compared. vm_deallocate is then used to rid of the
  904.  * two chunks of memory.
  905.  *
  906.  *************************************************************/
  907.  
  908. #include <mach.h>
  909. #include <stdio.h>
  910.  
  911. main(argc, argv)
  912.         int     argc;
  913.         char    *argv[];
  914. {
  915.         char            *data1, *temp;
  916.         char            *data2;
  917.         int             data_cnt, i, min;
  918.         kern_return_t   rtn;
  919.  
  920.  
  921.  
  922.         if (argc > 1) {
  923.                 printf("vm_read takes no switches.  ");
  924.                 printf("This program is an example vm_read\n");
  925.                 exit();
  926.         }
  927.  
  928.         if ((rtn = vm_allocate(task_self(), &data1, vm_page_size,
  929.                 TRUE)) != KERN_SUCCESS) {
  930.                 mach_error("vm_allocate returned value of ", rtn);
  931.                 printf("vmread: Exiting.\n");
  932.                 exit();
  933.         }
  934.  
  935.         temp = data1;
  936.         for (i = 0; (i < vm_page_size); i++)
  937.                 temp[i] = i;
  938.         printf("Filled space allocated with some data.\n");
  939.         printf("Doing vm_read....\n");
  940.         if ((rtn = vm_read(task_self(), data1, vm_page_size, &data2,
  941.                 &data_cnt)) != KERN_SUCCESS) {
  942.                 mach_error("vm_read returned value of ", rtn);
  943.                 printf("vmread: Exiting.\n");
  944.                 exit();
  945.         }
  946.         printf("Successful vm_read.\n");
  947.  
  948.         if (vm_page_size != data_cnt) {
  949.                 printf("vmread: Number of bytes read not equal to number");
  950.                 printf("available and requested.  \n");
  951.         }
  952.         min = (vm_page_size < data_cnt) ? vm_page_size : data_cnt;
  953.  
  954.         for (i = 0; (i < min); i++) {
  955.                 if (data1[i] != data2[i]) {
  956.                         printf("vmread: Data not read correctly.\n");
  957.                         printf("vmread: Exiting.\n");
  958.                         exit();
  959.                 }
  960.         }
  961.         printf("Checked data successfully.\n");
  962.  
  963.         if ((rtn = vm_deallocate(task_self(), data1,
  964.                 vm_page_size)) != KERN_SUCCESS) {
  965.                 mach_error("vm_deallocate returned value of ", rtn);
  966.                 printf("vmread: Exiting.\n");
  967.                 exit();
  968.         }
  969.  
  970.         if ((rtn = vm_deallocate(task_self(), data2,
  971.                 data_cnt)) != KERN_SUCCESS) {
  972.                 mach_error("vm_deallocate returned value of ", rtn);
  973.                 printf("vmread: Exiting.\n");
  974.                 exit();
  975.         }
  976. }
  977. 3.2. Virtual Memory Copying
  978.   The  vm_copy  primitive is another alternative to vm_read. In either case the
  979. task port used as the first parameter may specify the caller's address space or
  980. that  of  some other task. If another task's port is used vm_read copies memory
  981. from that task's address space to the caller's  address  space.  On  the  other
  982. hand,  vm_copy  moves the memory from one part of the designated task's address
  983. space to another section of that address space. The fact that these  primitives
  984. do   not  actually  copy  the  data  regions,  but  only  map  the  regions  as
  985. copy-on-write pages, means that they actually have some  use  even  in  copying
  986. data in the caller's own address space. If a task wants multiple virtual memory
  987. references to the same data, it can use either of these primitives to set  this
  988. up.  No  data  is  actually  copied  until  one  of the virtual memory areas is
  989. modified.
  990.  
  991.   The destination region must be allocated prior to the call to vm_copy.
  992.  
  993.     int             *data1, *data2;
  994.     kern_return_t   rtn;
  995.  
  996.     /* note that data2 was previously allocated. */
  997.  
  998.     if ((rtn = vm_copy(task_self(), data1, vm_page_size, data2)) !=
  999.             KERN_SUCCESS) {
  1000.             mach_error("vm_copy returned value of ", rtn);
  1001.             exit(1);
  1002.     }
  1003.  
  1004.  
  1005.   An complete example of a program using vm_copy  can  be  found  in  the  Mach
  1006. example directory as vm_copy.c.
  1007.  
  1008.  
  1009.  
  1010. 3.3. Inheritance of Shared Versus Copied Memory
  1011.   This  final virtual memory example illustrates the use of vm_inherit, and the
  1012. difference between copied and shared memory.  The problem posed is to have  two
  1013. memory  regions, one inherited and shared by a child task and one region simply
  1014. copied by the UNIX  fork  call.    To  show  the  difference  between  the  two
  1015. acquisition  methods,  the  parent  and  child will take turns printing out and
  1016. changing the values of the two regions.
  1017.  
  1018.   The first step towards a solution is to  allocate  and  fill  with  data  two
  1019. regions of memory.  The address of the memory that is to be shared by the child
  1020. task is passed to vm_inherit, using the VM_INHERIT_SHARED  flag.    The  shared
  1021. memory  is  used  in  this  program  as a lock to regulate whether the child or
  1022. parent process is to proceed.  After forking, the child  will  wait  while  the
  1023. parent  prints  out  the value of the shared memory and the value of the copied
  1024. memory.  After the parent is finished, he changes the lock causing  himself  to
  1025. wait and signaling the child to continue.  The child prints the contents of the
  1026. two regions then changes the lock and waits.    The  lock  change  signals  the
  1027. parent  to once again print the memory values.  The parent changes the lock and
  1028. exits.  The child notices the lock change, deallocates the two memory  regions,
  1029. and exits.
  1030.  
  1031.   Through  execution  of this program, the user will notice that the changes to
  1032. the copied memory are not seen from the child to the parent; that is  when  the
  1033. parent  changes  the  value,  the child does not see this change.  On the other
  1034. hand, any change in the shared memory is noticed by both tasks.
  1035.  
  1036.   The copied memory in this example is actually copy-on-write memory. That  is,
  1037. the  memory  is  never really copied until one of the tasks desires to write in
  1038. this region.
  1039.  
  1040.  
  1041. 3.3.1. Virtual Memory, Inheritance
  1042.   vm_inherit allows a task to specify how the various  regions  of  its  memory
  1043. will  be  passed  to  any  child  tasks that it forks. By default all memory is
  1044. passed to the child as a  logical  copy  (actually  copy-on-write).  vm_inherit
  1045. allows  a  task  to  specify  that certain page-regions of its memory are to be
  1046. shared with any children it subsequently forks, or are not to be passed at  all
  1047. to that child.
  1048.  
  1049.   The  inheritance parameter may be set to VM_INHERIT_SHARE, VM_INHERIT_COPY or
  1050. VM_INHERIT_NONE.  The size parameter is measured in  bytes  but  only  integral
  1051. numbers  of  pages  are dealt with. The pages are selected by rounding down the
  1052. start address to a page boundary and then rounding up the end address to a page
  1053. boundary.
  1054.  
  1055.     int             *lock;
  1056.     kern_return_t   ret;
  1057.  
  1058.     if ((ret = vm_inherit(task_self(), lock, sizeof(int),
  1059.             VM_INHERIT_SHARE)) != KERN_SUCCESS) {
  1060.             mach_error("vm_inherit returned value of ", ret);
  1061.             exit(1);
  1062.     }
  1063. 3.3.2. Programming Example IV, cowtest.c
  1064. /*
  1065.  * This program demonstrates the use of vm_inherit and copy on write
  1066.  * memory. A child and parent process will share memory, polling this
  1067.  * memory to see whos turn it is to proceed.  First some memory is allocated,
  1068.  * and vm_inherit is called on this memory, the variable 'lock'.  Next more
  1069.  * memory is allocated for the copy on write test. A fork is executed, and
  1070.  * The parent then stores new data in the copy on write memory
  1071.  * previously allocated, and sets the shared variable signaling to the
  1072.  * child that he is now waiting.  The child, polling the shared variable,
  1073.  * realizes it is his turn.  The child prints the value of the variable
  1074.  * lock and a value of the copy on write memory as the child sees it.
  1075.  * You will notice that the value of the lock is what the parent
  1076.  * set it to be, but the value of the copy on write memory is the original
  1077.  * value and not what the parent changed it to be.
  1078.  * The parent then awakes and prints out the two values once more.
  1079.  * The program then ends with the parent signaling the child via the
  1080.  * shared variable lock.
  1081.  ********************************************************/
  1082. #include <mach.h>
  1083. #include <stdio.h>
  1084.  
  1085. #define NO_ONE_WAIT 0
  1086. #define PARENT_WAIT 1
  1087. #define CHILD_WAIT 2
  1088. #define COPY_ON_WRITE 0
  1089. #define PARENT_CHANGED 1
  1090. #define CHILD_CHANGED 2
  1091.  
  1092. #define MAXDATA 100
  1093.  
  1094. main(argc, argv)
  1095.         int     argc;
  1096.         char    *argv[];
  1097. {
  1098.         int             pid;
  1099.         int             *mem;
  1100.         int             *lock;
  1101.         kern_return_t   ret;
  1102.  
  1103.         if (argc > 1) {
  1104.                 printf("cowtest takes no switches.  ");
  1105.                 printf("This program is an example of copy on write \n");
  1106.                 printf("memory and of the use of vm_inherit.\n");
  1107.                 exit();
  1108.         }
  1109.         if ((ret = vm_allocate(task_self(), &lock, sizeof(int),
  1110.                 TRUE)) != KERN_SUCCESS) {
  1111.                 mach_error("vm_allocate returned value of ", ret);
  1112.                 printf("Exiting with error.\n");
  1113.                 exit();
  1114.         }
  1115.         if ((ret = vm_inherit(task_self(), lock, sizeof(int),
  1116.                 VM_INHERIT_SHARE)) != KERN_SUCCESS) {
  1117.                 mach_error("vm_inherit returned value of ", ret);
  1118.                 printf("Exiting with error.\n");
  1119.                 exit();
  1120.         }
  1121.         *lock = NO_ONE_WAIT;
  1122.         if ((ret = vm_allocate(task_self(), &mem, sizeof(int) * MAXDATA,
  1123.                 TRUE)) != KERN_SUCCESS) {
  1124.                 mach_error("vm_allocate returned value of ", ret);
  1125.                 printf("Exiting with error.\n");
  1126.                 exit();
  1127.         }
  1128.         mem[0] = COPY_ON_WRITE;
  1129.  
  1130.         printf("value of lock before fork: %d\n", *lock);
  1131.         pid = fork();
  1132.         if (pid) {
  1133.                 printf("PARENT: copied memory =  %d\n",
  1134.                         mem[0]);
  1135.                 printf("PARENT: changing to %d\n", PARENT_CHANGED);
  1136.                 mem[0] = PARENT_CHANGED;
  1137.                 printf("\n");
  1138.                 printf("PARENT: lock = %d\n", *lock);
  1139.                 printf("PARENT: changing lock to %d\n", PARENT_WAIT);
  1140.                 printf("\n");
  1141.                 *lock = PARENT_WAIT;
  1142.                 while (*lock == PARENT_WAIT)
  1143.                    /* wait for child to change the value */;
  1144.                 printf("PARENT: copied memory = %d\n",
  1145.                         mem[0]);
  1146.                 printf("PARENT: lock = %d\n", *lock);
  1147.                 printf("PARENT: Finished.\n");
  1148.                 *lock = PARENT_WAIT;
  1149.                 exit();
  1150.         }
  1151.         while (*lock != PARENT_WAIT)
  1152.                 /* wait for parent to change lock */ ;
  1153.         printf("CHILD: copied memory = %d\n", mem[0]);
  1154.         printf("CHILD: changing to %d\n", CHILD_CHANGED);
  1155.         mem[0] = CHILD_CHANGED;
  1156.         printf("\n");
  1157.         printf("CHILD: lock = %d\n", *lock);
  1158.         printf("CHILD: changing lock to %d\n", CHILD_WAIT);
  1159.         printf("\n");
  1160.         *lock = CHILD_WAIT;
  1161.         while (*lock == CHILD_WAIT)
  1162.                 /* wait for parent to change lock */ ;
  1163.         if ((ret = vm_deallocate(task_self(), lock,
  1164.                 sizeof(int), TRUE)) != KERN_SUCCESS) {
  1165.                 mach_error("vm_deallocate returned value of ", ret);
  1166.                 printf("Exiting.\n");
  1167.                 exit();
  1168.         }
  1169.         if ((ret = vm_deallocate(task_self(), mem,
  1170.                 MAXDATA * sizeof(char), TRUE)) != KERN_SUCCESS) {
  1171.                 mach_error("vm_deallocate returned value of ", ret);
  1172.                 printf("Exiting.\n");
  1173.                 exit();
  1174.         }
  1175.         printf("CHILD: Finished.\n");
  1176. }
  1177. 4. Mach environment
  1178.   See  the  section  General Mach Information at the end of the A Programmers's
  1179. Guide to the Mach User Environment to find the current include file  names  and
  1180. paths needed to compile code using the Mach system calls.
  1181.                                Table of Contents
  1182. 1. Introduction                                                               1
  1183.      1.1. Tutorial documents                                                  1
  1184.      1.2. Basic Mach Concepts                                                 1
  1185. 2. Message Communication Between Processes                                    1
  1186.      2.1. Mach Error Printing                                                 2
  1187.      2.2. Port Allocation                                                     2
  1188.      2.3. Port Deallocation                                                   2
  1189.      2.4. Environment Manager Server/Checking in a Port                       2
  1190.      2.5. Environment Manager Server/Looking up a Port                        2
  1191.      2.6. Setting up a Simple Message                                         2
  1192.      2.7. Sending Messages                                                    2
  1193.      2.8. Receiving Messages                                                  2
  1194.      2.9. Setting up a Reply Message                                          3
  1195.      2.10. RPC, Send/Receive                                                  3
  1196.      2.11. A Non-Simple Message                                               3
  1197.      2.12. Programming Example I, simp_ipc.c                                  4
  1198. 3. Use of Virtual Memory                                                      6
  1199.      3.1. Allocation, Deallocation, and Reading                               6
  1200.           3.1.1. Virtual Memory Allocation                                    6
  1201.           3.1.2. Virtual Memory Deallocation                                  6
  1202.           3.1.3. Virtual Memory Reading                                       6
  1203.           3.1.4. Programming Example II, vm_read.c                            7
  1204.      3.2. Virtual Memory Copying                                              8
  1205.      3.3. Inheritance of Shared Versus Copied Memory                          8
  1206.           3.3.1. Virtual Memory, Inheritance                                  8
  1207.           3.3.2. Programming Example IV, cowtest.c                            9
  1208. 4. Mach environment                                                          10
  1209.