A GUI terminal emulator for such an architecture needs at least two open file descriptors, one for the connection to the X server and one for the master side of the pseudo-terminal. The other 14, it turned out, were being used by VTE's scrollback buffer mechanism, which involves writing data that have scrolled off the top of the screen out to (temporary) files.
They had managed to reduce this, by rearranging the structures of the scrollback files, to 8 open file descriptors per emulation by 2011, and reportedly it will be soon down to 4.
Interesting tidbit #1:
It was mentioned elsewhere in this discussion that the alternate screen on many terminal emulators has no scrollback. This is because the programs that switch between primary and alternate screens aren't actually doing that as far as they are concerned. They are switching between scrolling mode and cursor addressing mode (see http://superuser.com/a/715563/38062 for details), the latter not really having the concept of a negative row coördinate.
The VTE widget was using twice the number of open file descriptors, because both scrolling and cursor addressing modes had scrollback files.
* https://bugzilla.redhat.com/show_bug.cgi?id=667539
* https://bugzilla.gnome.org/show_bug.cgi?id=646098
* https://bugzilla.gnome.org/show_bug.cgi?id=738601
* https://bugzilla.gnome.org/show_bug.cgi?id=741520
Interesting tidbit #2:
LXTerminal has this single centralized emulator process architecture, too. It has a rather nasty open file descriptor leak with which one can render LXTerminal completely unusable in about 1 minute (if one has an open file descriptor limit of 1024).
Incidentally, AmigaOS' terminal design is worth exploring - it is a fascinating example of layering. And the AROS re-implementation, while not very clean, is also partially object oriented (in C; disclaimer: I wrote part of it) in how it layers the console units from the simplest to most complete (cut and paste support + scrollback). Even back in the 80's performance was good enough for this that AmigaOS started using dispatch-function based OO all over the place, and the AROS re-implementation of the console code uses that method, which is not at all the fastest way of doing it, but it's fast enough even on real M68k's.
Any modern PC is going to be at a minimum several hundred times faster.
All you need is to maintain a linked list of lines, and add code to free or reuse when you reach the maximum size of the scrollback buffer. If you want you can easily also just use a ring-buffer and wrap around if you want to set a size limit in bytes instead of lines, and just maintain indexes into it. It's trivial to do this in ways that doesn't cause performance issues.