home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / utility / disk / pdos_112 / pipe_use.txt < prev    next >
Text File  |  1993-07-30  |  22KB  |  491 lines

  1. To install pipes:
  2.  
  3. 1. copy the following four lines to the end of the CONFIGUR file:
  4.  
  5. Install pipes. The first line installs the pipe file manager, the
  6. second creates a pipe device named "PIPE".
  7. #pipe.dev
  8. #makepipe.ttp $pipe
  9.  
  10. 2. Copy the files PIPE.DEV and MAKEPIPE.TTP into the POWERDOS folder.
  11.  
  12. 3. Reboot the machine.
  13.  
  14. You will now have a new device in the system, called PIPE: which is
  15. used to create pipes.
  16.  
  17. There are two kinds of pipes, named and unnamed. Unnamed pipes are
  18. used primarily by command line shells to set up a pipeline of
  19. commands using processes called filters. Filters are simple programs
  20. that read data from their standard in, modify it in some way, and
  21. write it to their standard out. Unnamed pipes allow you to tie the
  22. standard out of one process to the standard in of another. The pipe
  23. file manager synchronizes the two processes to keep the data moving
  24. between them. Following is an example sequence that would launch two
  25. tasks concurrently with an unnamed pipe between them:
  26.  
  27. ************************************************************************
  28. *
  29. * Test program using pipes to tie two processes together. First process
  30. * gets its standard in from a text file, converting all lowercase
  31. * characters to uppercase; it then wrties to its standard out, which is
  32. * tied (through an unnamed pipe) to the standard in of a process which
  33. * converts all uppercase characters to lowercase.
  34. *
  35. * Note that under normal GemDOS, an error is NOT returned when the
  36. * end of a file is reached, thus a program reading from its standard in
  37. * has no way of knowing if the EOF has occured, without some added
  38. * overhead. Under PowerDOS, any open disk file can be made to report EOF
  39. * unsing an Fstatus call to change an option bit. Once this is done, the
  40. * process can read from its standard in until the EOF error is returned,
  41. * without any other work necessary.
  42. *
  43. ************************************************************************
  44.  
  45.             OPT         C-
  46.  
  47.             include     dosmacro.s
  48.  
  49.             include     dos_defs.s
  50.  
  51.             tos_init
  52.  
  53. Start       c_conws     clearsc(pc)             Clear the screen
  54.             f_open      #0,text_file(pc)        Open the text file
  55.             move.w      d0,d7                   Save its handle
  56. * +++
  57. *
  58. * The following Fstatus operation has the open file report EOF as an error,
  59. * instead of just reporting zero bytes read.
  60. *
  61. * ---
  62.             f_status    do_eof(pc),#st_wrtopt,d7
  63. * +++
  64. *
  65. * An unnamed pipe is created by just not giving it a name....
  66. *
  67. * ---
  68.             f_open      #0,pipe_name(pc)        Open an unnamed pipe
  69.             move.w      d0,d6                   Save its handle
  70. * +++
  71. *
  72. * We want to make copies of the original standard in and out handles so we
  73. * can restore them when needed.
  74. *
  75. * ---
  76.             f_dup       #0                      Copy standard in
  77.             move.w      d0,d5
  78.             f_dup       #1                      Copy standard out
  79.             move.w      d0,d4
  80. * +++
  81. *
  82. * First process in pipeline gets input from the file, sends it to the pipe...
  83. *
  84. * ---
  85.             f_force     d7,#0                   Standard in comes from the file
  86.             f_force     d6,#1                   Standard out goes to the pipe
  87. * +++
  88. *
  89. * Execute filter as concurrent task with parent; it will have same priority
  90. *
  91. * ---
  92.             p_exec      null(pc),null(pc),upper(pc),#$10
  93. * +++
  94. *
  95. * Second process gets input from the pipe, sends it to original standard out...
  96. *
  97. * ---
  98.             f_force     d4,#1                   Restore standard out
  99.             f_force     d6,#0                   Standard in comes from the pipe
  100. * +++
  101. *
  102. * Execute second process concurrently; the pipe file manager will synchronize
  103. * execution of each process, so the output of the first does not overrun the
  104. * input to the second...
  105. *
  106. * ---
  107.             p_exec      null(pc),null(pc),lower(pc),#$10
  108. * +++
  109. *
  110. * Standard out has already beed restored to original handle; we need only
  111. * restore our standard in (our child processes inherit our standard handles
  112. * at the time of their execution; after they are running, what we do with our
  113. * handles does not affect them), and get rid of the handles we no longer need...
  114. *
  115. * ---
  116.             f_force     d5,#0                   Restore standard in
  117.             f_close     d4                      Get rid of copy of standard out
  118.             f_close     d5                      Get rid of copy of standard in
  119.             f_close     d6                      Get rid of the pipe
  120.             f_close     d7                      Get rid of opened file
  121. * +++
  122. *
  123. * Nothing to do till both child processes have fininshed, so just wait for both
  124. * of them...
  125. *
  126. * ---
  127.             wait                                Wait for first child to term
  128.             wait                                Wait for second child to term
  129.  
  130.             c_conin                             Wait for a key
  131.             p_term      #0                      Kill ourselves
  132.  
  133. do_eof      dc.w        0,0,1
  134. null        dc.b        0,0
  135. pipe_name   dc.b        "pipe:",0
  136. upper       dc.b        "to_upper.prg",0
  137. lower       dc.b        "to_lower.prg",0
  138. text_file   dc.b        "testfile.txt",0
  139. clearsc     dc.b        27,"E",0
  140.  
  141.             end
  142.  
  143.  
  144.  
  145. Here are the two filter programs:
  146.  
  147.  
  148.  
  149. ************************************************************************
  150. *
  151. * Program to read from standard input and write to standard output.
  152. * Changes any lowercase characters read into uppercase.
  153. *
  154. ************************************************************************
  155.  
  156.             include     dosmacro.s
  157.  
  158.             tos_init
  159.  
  160. Start       lea         string(pc),a5           Point to string space
  161.             move.b      #200,(a5)               Read up to 200 chars
  162. .loop       c_conrs     (a5)                    Read a line in
  163.             tst.w       d0                      Was there an error?
  164.             bmi.s       .exit                   Exit if so
  165.             lea         2(a5),a0                Point to actual string
  166.             bra.s       .do_count               Jump into loop
  167. .case_loop  move.b      (a0)+,d1                Get next char
  168.             cmp.b       #"a",d1                 Lower than a?
  169.             blo.s       .do_count               Yes, don't change
  170.             cmp.b       #"z",d1                 Higher than z?
  171.             bhi.s       .do_count               Yes, don't change
  172.             andi.b      #$df,d1                 Clear lowercase bit
  173.             move.b      d1,-1(a0)               Back to string
  174. .do_count   dbra        d0,.case_loop           Count down chars
  175.             c_conws     2(a5)                   Write modified string
  176.             c_conws     crlf(pc)                And a cr/lf
  177.             bra.s       .loop                   Next line
  178. .exit       p_term      #0
  179. crlf        dc.b        $d,$a,0
  180. string      ds.b        204                     Enough space for the string
  181.  
  182.             end
  183.  
  184.  
  185. ************************************************************************
  186. *
  187. * Program to read from standard input and write to standard output.
  188. * Changes any uppercase characters read into lower case.
  189. *
  190. ************************************************************************
  191.  
  192.             include     dosmacro.s
  193.  
  194.             tos_init
  195.  
  196. Start       lea         string(pc),a5           Point to string space
  197.             move.b      #200,(a5)               Read up to 200 chars
  198. .loop       c_conrs     (a5)                    Read a line in
  199.             tst.w       d0                      Was there an error?
  200.             bmi.s       .exit                   Exit if so
  201.             lea         2(a5),a0                Point to actual string
  202.             bra.s       .do_count               Jump into loop
  203. .case_loop  move.b      (a0)+,d1                Get next char
  204.             cmp.b       #"A",d1                 Lower than A?
  205.             blo.s       .do_count               Yes, don't change
  206.             cmp.b       #"Z",d1                 Higher than Z?
  207.             bhi.s       .do_count               Yes, don't change
  208.             ori.b       #$20,d1                 Set lowercase bit
  209.             move.b      d1,-1(a0)               Back to string
  210. .do_count   dbra        d0,.case_loop           Count down chars
  211.             c_conws     2(a5)                   Write modified string
  212.             c_conws     crlf(pc)                And a cr/lf
  213.             bra.s       .loop                   Next line
  214. .exit       p_term      #0
  215. crlf        dc.b        $d,$a,0
  216. string      ds.b        204                     Enough space for the string
  217.  
  218.             end
  219.  
  220.  
  221.  
  222. This code was produces and assembled with the Devpac development
  223. package. Note: the files dosmacro.s and des_defs.s are included with
  224. the arc that contained this text file.
  225.  
  226. Named pipes
  227.  
  228. While unnamed pipes are useful for building command pipelines from a
  229. shell program, that aren't much good for general purpose interprocess
  230. communication. The reason is simple: since unnamed pipes have no
  231. name, they cannot be created by one process, then opened by another.
  232. Once an unnmaed pipe is created, handles to it may be passed to child
  233. processes, but thats it. Even the creator of it cannot do another
  234. open to get another handle to it. If you open three unnamed pipes in
  235. a row, you get three distinct pipes. Also, unnamed pipes are limited
  236. in the size of the circular buffer which is used to move data through.
  237.  
  238. Named pipes have none of the restrictions of unnamed pipes. Since
  239. they have a name, they can be created by a process, and left in the
  240. system when the process terminates. Other processes can open them,
  241. read from or write to them, list them and see if there is any unread
  242. data in them using the Fsfirst/snext commands, and set the size of the
  243. circular buffer when they are created.
  244.  
  245. So if named pipes are so flexible, why have unnamed pipes? Because
  246. named pipes require allocation of memory (with unnamed pipes, the
  247. circular buffer exists entirely within the path descriptor structure),
  248. and because a command line shell would require extra overhead to
  249. insure that the name it was giving to a pipe to be used for a pipeline
  250. was not already in use. It is easier to create any number of unnamed
  251. pipes because no name is involved.
  252.  
  253. Several things to keep in mind about named pipes:
  254.  
  255. The name is a normal GemDOS 8.3 name.
  256.  
  257. They will always have at least 256 bytes of buffer, even if a smaller
  258. size is requested.
  259.  
  260. The pipe file manager knows how many users of a pipe there are, and
  261. if you try to read from a pipe that has no (current) possibility of a
  262. writer, you will receive an EOF error. Simply put, if the number of
  263. readers is the same as the number of users, you will get an EOF error.
  264.  
  265. If you are the last user of an empty pipe, and you close it, it will
  266. vanish from existance. However, if there is data in the pipe and you
  267. close it, it will remain in memory until someone opens it, reads the
  268. remaining data, and then closes it. After this, it will terminate.
  269.  
  270. If you are writing to the pipe, and you fill the buffer, but there
  271. are no readers, you will wait (forever maybe) until another process
  272. opens the pipe and reads the buffer, thus allowing you to complete
  273. the write. Unlike readers, which return eof if there is no writer,
  274. writers wait until there is a reader (this is not true of unnamed
  275. pipes, since if the number of writers = the number of users, it would
  276. be impossible for another process to open the pipe to read the data;
  277. thus, in this case, an error MUST be returned).
  278.  
  279.                         **************
  280.  
  281. So, what use is there for a named pipe? Well, one of the most useful
  282. things I can think of is to use a pipe to implement client/server
  283. functions over a network. This would involve the use of two pipes,
  284. both created by the server. One would be a pipe that the server reads
  285. from, and the client writes to (the command pipe), the other would be
  286. a pipe that the server writes to and the client reads from (the
  287. results pipe). To make sure that a minimal amount of process
  288. switching occures, we would also make sure that the cammand and
  289. result pipes had buffers that are larger (say, by 20%) than the data
  290. that will be placed in them.
  291.  
  292. As an example of how this works, I created a way to get process
  293. information on processes running on other machines over a network.
  294. The process monitor (which is provided with PowerNet) was modified to
  295. look for these command/result pipes on other machines on the network.
  296. If they existed, it would open them, write a command to the command
  297. pipe, then wait for the results from the result pipe.
  298.  
  299. Following is the source code for the server task. It is amazingly
  300. short, given the fact that it provides a service to another process
  301. over a network. It would be easy to expand on this idea to provide
  302. far more complex services in a networked environment:
  303.  
  304.  
  305. ************************************************************************
  306. *
  307. * Background process monitor.
  308. * This program sets itself up in the background, and creates two named
  309. * pipes called "COMMAND.BSP" and "RESULTS.BSP". It reads the command
  310. * pipe for requests to fetch information on a specific process.
  311. *
  312. * A single word is sent by a requestor. The word is the process ID
  313. * of the process to get the information for.
  314. *
  315. * The results of this call is written to the results pipe. An entire
  316. * process information structure is written, even if the call resulted
  317. * in an error. In the case of an error, the error code (a byte) is
  318. * returned in the pi_queueID code byte of the process information
  319. * structure
  320. *
  321. * The return block is defined in the file 'DOS_DEFS.S'. It is the
  322. * structure returned from a p_procinf call. Its size is set in
  323. * DOS_DEFS.S as the variable 'pi_size'.
  324. *
  325. ************************************************************************
  326.  
  327.             include     dos_defs.s
  328.  
  329.             include     dosmacro.s
  330.  
  331.             RSRESET
  332. buffer      rs.b        pi_size     Space to place process info into
  333.             rs.b        200         Space for a stack
  334. block_size  equ         __RS
  335.  
  336. start       bra         initial                 Do initializing stuff
  337. the_rest    m_shrink    d5,start-256(pc)        Free unused memory
  338.             s_send      d3,d4                   Wake parent up
  339. .loop       f_read      buffer(a6),#2,d7        Try to read a command
  340.             move.w      buffer(a6),d0           Get process to read
  341.             p_procinf   buffer(a6),d0           Get info on it
  342.             tst.w       d0                      Work ok?
  343.             bpl.s       .send
  344.             move.b      d0,buffer+pi_queueID(a6) Return error here
  345. .send       f_write     buffer(a6),#pi_size,d6  On its way!
  346.             bra.s       .loop                   Next command
  347.  
  348. * From the label 'start' to this point, plus the 'block_size' structure
  349. * listed above, plus the 256 byte basepage, is all that remains of this
  350. * background task once it is installed. Currently, this is 654 bytes. 
  351.  
  352. variable:
  353.  
  354. initial     lea         start-256(pc),a0        Get basepage
  355.             move.l      #end_of_it-start+10000,d0
  356.             lea         0(a0,d0.l),sp           Temp stack
  357.             m_shrink    d0,(a0)                 Free memory up before we make pipes
  358.             c_conws     sign_on(pc)             Sign-on message to screen
  359.             f_create    #0,command_pip(pc)      Create the command pipe
  360.             move.w      d0,d7
  361.             bmi.s       .error
  362. * We'll open the command pipe again. If you try to read from a pipe that
  363. * has no other users, it will return an EOF error. We want this process
  364. * to be able to stay dormant while it waits for commands to be sent
  365. * through the command pipe. An EOF error would not make this possible,
  366. * as the process would have to check for the error, do a delay, then
  367. * make the call again, etc. If we simply open the pipe again, the pipe
  368. * file manager will note that there is another owner and will not
  369. * return the EOF error.
  370.             f_open      #1,command_pip(pc)      Don't bother keeping the handle
  371.             f_create    #0,results_pip(pc)      And the results pipe
  372.             move.w      d0,d6
  373.             bmi.s       .error
  374.             lea         variable(pc),a6
  375.             move.l      #(variable-start)+256+block_size,d5 Memory size to keep
  376.             lea         block_size(a6),sp       Put stack at the top of vars
  377.             p_getppid                           Get our parent's process ID
  378.             move.w      d0,d4
  379.             moveq       #S_wake,d3              Signal to send
  380.             bra         the_rest
  381. .error      c_conws     error(pc)               No good, error out
  382.             p_term      #0
  383. end_of_it   nop
  384.  
  385. command_pip dc.b        "pipe:\command.bsp\10",0 Commands are only two bytes
  386. results_pip dc.b        "pipe:\results.bsp\500",0 At least pi_size bytes
  387.  
  388. sign_on     dc.b        "PowerDOS Background Process Monitor",$d,$a,0 
  389. error       dc.b        "Unable to open communications pipe",$d,$a,0
  390.  
  391.             end
  392.             
  393.  
  394. Two things to note about the above program: you can see by the pipe
  395. file names that the requested size of the pipe is given as a decimal
  396. value after the pipe name. If the name is not followed by a size,
  397. then the default 256 byte buffer is used. Note that if you request
  398. less than 256 bytes, 256 bytes becomes the size, thus the \10 given
  399. in the cammand pipe name has no real purpose, other than as a
  400. reference.
  401.  
  402. The other point of note is that the command pipe was opened twice.
  403. The reason is as listed. If you are the only current user (one opened
  404. handle) of the pipe and you try to read from it, you'll get an eof
  405. error returned. That can be useful, but in this case the process has
  406. nothing to do while waiting for a command, so it should stay dormant
  407. until a command is present. If it is dormant, it uses no CPU time;
  408. its only used resource becomes the memory it occupies. The pipe file
  409. manager will wake it up as soon as any data is written to the pipe.
  410. By opening the pipe twice, the file manager sees that there is
  411. another user (or opened handle) of the pipe. The pipe file manager
  412. assumes that the other user (handle) of the pipe might start writing
  413. at any time, so the reader is allowed to wait. The pipe file manager
  414. does not care that the other user is the same user that is trying to
  415. read (which means in this case that the number of readers, in fact,
  416. does equal owners). This is not a bug; it was for this very reason
  417. that the pipe file manager does not take into account WHO owns the
  418. handles, only that readers does not equal owners.
  419.  
  420. Now, I said that there could be times that it would be good to get an
  421. EOF error from the command pipe. Lets say we had a server that did
  422. many things. Using the above technique, the server would be stuck
  423. waiting for a process to write a command, and would not be able to do
  424. anything else. By not having the pipe opened twice, it becomes very
  425. easy to detect if there may be data available (i.e. another user has
  426. the pipe opened). We simply do the read, and if our return (in d0.l)
  427. is negative (the actual EOF error is defined in the DOS_DEFS.S file),
  428. we know that there is not currently another user, thus no data to
  429. read. We could go on to do other things, in a loop, coming back
  430. periodically to check the pipe.
  431.  
  432. Ok, you ask, but what if the pipe gets opened over the network, but
  433. no commands are sent? Well, in that case, the server would get stuck.
  434. There are two ways around this.
  435.  
  436. 1. Never, as a client, open the command pipe and leave it open. You
  437. could open it, send a command, receive the results and close it again.
  438.  
  439. 2. Forget number one. Have the server NOT read the command pipe if it
  440. does not contain a command.
  441.  
  442. Ok, how do we do number 2? By using the Fstatus call (it is called
  443. f_status in the provided macro file). Lets say register d7 contained
  444. your command pipe file handle. Simply do the following:
  445.  
  446.     f_status    #st_input,d7            See if data is available
  447.     tst.l       d0                      If true (<>0), data is available
  448.     beq.s       no_data
  449.     f_read      buffer(pc),#size,d7     Read command from pipe
  450.     ....
  451.  
  452. The 'st_input' constant is defined in DOS_DEFS.S along with some
  453. other usefull status operation subcodes.
  454.  
  455. Ok, what do we do if there is nothing to do, but we don't want to get
  456. stuck waiting for data to become available, but we also don't want to
  457. soak up a bunch of CPU time while waiting for something to happen?
  458. This is easy. First, if you are a background task, you could lower
  459. your priority like this:
  460.  
  461.     p_priority  #0,#0                   Get our priority
  462.     subq.w      #2,d0                   Lower it a little
  463.     p_priority  d0,#0                   Set new, lower priority for us
  464.  
  465. If your priority was 100, and the forground applications was also
  466. 100, you would both get half the CPU time (assuming no other
  467. processes). By lowering yours by two, to 98, you will run only half
  468. as often as the other process, or 1/3 of the CPU time would be yours
  469. (because processes are aged before priority comparisons are done,
  470. lowering your priority by one would have no effect).
  471.  
  472. What is a lot more usful, however, is to RAISE your priority. Since
  473. GEM programs are CPU hogs, but since they are very user input
  474. dependant, it is sometimes better to be able to preempt the GEM
  475. process whenever you need to run, but to pass off the CPU when you
  476. don't need to run, using the following command:
  477.  
  478.     sleep   #100                Pause for 100 milliseconds (1/10 second)
  479.  
  480. The value following the sleep macro is the number of milliseconds you
  481. wish to be dormant. Your server program could thus do its loop,
  482. checking for stuff to do, like read a command from the command pipe,
  483. then free the CPU for other, less important stuff, like the foreground
  484. application (ha ha). You can adjust the number of milliseconds to
  485. wait to whatever the requirements of your server might be. The longer
  486. the delay, the less often you'll check for things to do. Conversely,
  487. the shorter the delay, the more often you'll hog the CPU in a loop
  488. that ends up with nothing to do.
  489.  
  490.  
  491.