⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 readme.now

📁 增强的pterm01b.zip 终端仿真程序(63KB)<END>
💻 NOW
📖 第 1 页 / 共 3 页
字号:

      Also, both of Williams threads use overlapping (asynchronous) I/O,
      resulting in an even more efficient set up.

      Now for the threads I spawn. They are as follows:

         o  Input thread

            Because I did not want to muck with the input queue being
            managed by the communications library, I created another layer.
            My input thread simply waits for characters to appear in the
            main input queue, and block copies them into a local queue used
            by the display thread (below). If there are no characters
            waiting in the main input queue, this thread sleeps until there
            is (in the discussions which follow, this thread will be
            referred to as 'my input thread', to differentiate it from the
            main input thread operating in the library). It may be that I
            can increase PTERM's efficiency slightly by removing this layer
            and having the display thread pull characters directly from the
            library managed input queue -- something I will consider.


         o  Display thread

            This thread is solely responsible for removing characters from
            the local input queue (managed above) and deciding what to do
            with them. For instance, if the beginning of an ANSI escape
            sequence is detected this thread passes control to a function
            whose job it is to interpret the sequence (note: the ANSI code
            still executes within the display thread, no other thread has
            been created). One of the other jobs of the display thread is
            to optimize the output into the console window. This is done in
            a very simplistic manner. In the absence of ANSI escape
            sequences, the display thread will accumulate characters from
            the local input queue into a buffer -- up to 256. When the
            limit of 256 has been reached, or a special sequence is
            detected, the display thread blasts the buffer to the screen in
            a single call to WriteFile(). This is MUCH more efficient than
            calling something like putch() for each single character.
            However, at slow speeds (1200 and 2400 baud), it takes a
            noticeable amount of time to accumulate 256 characters, and
            this is the reason for the choppy display at these speeds. If
            you set the port to anything less than 9600 baud, PTERM reduces
            this 'blast' count to 64 characters, producing a smoother
            display.

            As if this wasn't enough, the display thread has one more job to
            perform. It watches for the tell-tale sequence of characters
            which signal the host is preparing for a Zmodem upload or
            download. If this sequence is found, it starts the appropriate
            code.

            If there are no characters in the local input queue, the display
            thread sleeps until there are.


         o  User thread

            The user threads job is pretty easy. If the user hits a key,
            decide what to do with it. Typically the key will be sent right
            out the port. If it is a control key sequence (like ALT-X), then
            the user thread executes the code appropriate for that key. This
            thread blocks on the keyboard, and as such sleeps when there are
            no characters available.


         o  Main thread

            The main thread is the first thread which executes (starting in
            main()). It initializes the port and other things, starts up the
            three threads above, and then goes to sleep waiting for all
            three of the threads above to terminate. At this point it wakes
            up and calls ExitProcess(), ending PTERM.


      Them's the threads, and a nice bunch of threads they are. However, if
      there is one thing I quickly learned from spawning threads all over
      the place, its that synchronization thingy.

      Here's the scenario: My input thread pulls characters from the main
      input queue to place in the local input queue. Fair enough. So, I
      start a Zmodem download. Boom! Of course, the Zmodem download code
      also wants to pull characters from the main input queue, so my thread
      fights with the Zmodem code. Remember, the Zmodem code is run as part
      of the display thread. So my input thread grabs some characters, then
      the Zmodem code [running in the display thread] grabs some
      characters, and so on. Zmodem will end up missing a bunch of
      characters it expected to see, and my input thread grab some
      characters from the transfer which the display thread will finally
      get and try to display. A mess...

      So, I need to find a way to synchronize the two threads. When Zmodem
      wants control of the main input queue, it needs to 'ask' for control
      from my input thread. Well, it really isn't as formal as all that.

      Just before my input thread grabs characters from the main input
      queue, it waits on a semaphore. Simplified, a semaphore is just an
      object which keeps count of how many threads have asked for control
      of it. When its count is 0, access is granted to the waiting thread.
      When a thread gets access to it, the semaphores count is incremented.
      Any other thread which waits on it goes to sleep until the semaphores
      count becomes 0 again. The count is decremented when a thread
      explicitly releases a semaphore, thus allowing another thread to gain
      access (this can get complicated when there are more than 2 threads).

      Ok, where were we? Oh, yes, my input thread waits on this semaphore
      gadget. Typically, it gets control instantly and drops into the main
      body of its code where it grabs characters from the main input queue
      and stuffs them in the local queue the display thread uses. Once it
      has transferred some characters, it releases the semaphore (and gives
      up the rest of its time slice). Its during the time between releasing
      the semaphore and waiting on it again that the file transfer code
      must act.

      The first line of code for a file transfer waits on the same
      semaphore. As soon as my input thread releases the semaphore, the file
      transfer code gets and holds access to it until the transfer is
      completed, thus preventing my input thread from mucking with the main
      input queue during the transfer. Whew!

      Being this is my first foray into threads, I am very interested to
      find out if my code breaks on a multi-processor machine. Does my code
      work now because it takes advantage of the synchronous behavior of a
      single processor? Interesting stuff, but I forgot to pick up my
      Sequent 16 processor monster-box at the grocery store the other day,
      so I suppose the answer will have to wait...

      I found one other interesting thing about Windows NT -- it seems to
      like to write data to the disk right away. Generally, this is a good
      thing, keeps your data safe. However, with Zmodem code writing 1K
      blocks to disk every .7 seconds or so (at 1650 cps), the disk access
      began to affect the performance of the download. The solution was
      easy, I just used a call to setvbuf() to create a memory buffer. The
      size I chose is 64K. So the system (C runtime) will accumulate 64K
      bytes from the fwrite()'s before writing to disk. Believe it or not,
      NT can actually write a 64K block to disk as quickly as a 1K block --
      difference is, now Zmodem only hits the disk one a minute or so (at
      1650 cps). Works great. Notice I said I made a 64K buffer. You 16 bit
      guys probably have (as I did) the signed integer maximum value
      memorized, 32K right? Well, under NT, a signed integer is 2^31, for a
      maximum [signed] value of 2 GB. So 64K was small compared to what
      I could make it.

      The file access areas of the transfer protocols are excellent
      candidates for overlapped I/O, probably doing away with the need for a
      write buffer. However, since Williams library is meant to be
      multi-platform, it is best to keep it as generic as possible. But,
      it is C++, and with proper inheritance the function responsible for
      writing data to disk could be overloaded (and William has isolated
      this operation for easy overloading!). A possible future enhancement
      to PTERM.

      One more technical note: PTERM does not change its base priority
      class, but it does manipulate the priority of several of its threads
      relative to the priority class it is started at. What? Ok, if you run
      PTERM from the command line like so:

         start pterm.exe

      then PTERM gets a base priority class of NORMAL. Using these commands:

         start /high pterm.exe
         start /realtime pterm.exe

      starts PTERM with a base priority class of HIGH or REALTIME
      respectively.

      Whats interesting is that when I started work on PTERM, I assumed I
      would need to have PTERM boost its base priority class to at least
      HIGH to make sure it could respond quick enough to bytes coming in
      over the port at high baud rates. This resulted in poor performance
      (dropping characters and such). So I assumed PTERM wasn't getting the
      priority necessary to keep up with bytes coming in the port. So, I
      pumped the base priority class up to REALTIME. The performance was
      even worse!

      Suddenly, it began to make sense. This is what I figure is happening:
      The serial driver itself probably runs at the upper end of NORMAL
      priority, or maybe even the lower end of HIGH priority. So I figure
      when I boosted the priority of PTERM to the same or above the priority
      of the serial driver, PTERM was stealing away time from the serial
      driver, causing the driver itself to drop characters!

      This is pure speculation, since I have no detailed knowledge of what
      priority the serial driver runs at. I assume there is a small part of
      the serial driver which is interrupt driven, and depending on the
      interrupt level it runs at, it can be preempted by other processes.
      I further assume that some other piece of the serial driver gets
      scheduled by the interrupt driven part above to remove characters from
      some small local buffer and place them in the buffer which ReadFile()
      uses. Again, this is pure speculation.

      At any rate, the interesting part is that when I changed PTERM to NOT
      change its base priority class, so that upon starting PTERM, it would
      default to the NORMAL priority class, performance was MUCH better!

      What does this tell me? The NT serial driver is incredibly efficient,
      yet can become sensitive to other high priority processes in the
      system (the behavior I described above was on my 386-33 -- this may not
      be an issue on faster machines, where higher priority processes might
      not have such adverse affects on the serial driver because the faster
      processor is able to juggle all the processes more efficiently).

      At any rate, what it means to you the user is that you are free to
      start PTERM at any of the 4 base priority classes, and your mileage
      may vary. Examples:

         start /idle pterm.exe
         start /normal pterm.exe
         start /high pterm.exe
         start /realtime pterm.exe

      I strongly recommend leaving it at NORMAL priority (by using the
      /normal switch, or not giving a priority at all). You are, of course,
      free to experiment.

      Well, the rest of PTERM is plain and boring (as if the preceding was
      not!). So not much else to say. Any specific questions? Feel free to
      contact me!

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -