1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
.help pr_psio
.nf __________________________________________________________________________
PR_PSIO -- Pseudofile i/o for a process. Process an i/o request for the
specified pseudofile stream of the specified process. Called either to read
command input from the CLIN of a process, or to process a read or write
request to a pseudofile of a process.
1. Introduction
Pseudofile i/o in a multiprocess configuration, e.g., for the graphics
streams, is quite complex and difficult to explain briefly. I have tried to
cover the major points here but warn the reader that it is not going to be easy
to understand the flow of data and control involved. The problem is a difficult
one due to the nature of the IPC protocol and the complexity of the three
process architecture required when an external graphics kernel is used. The
discussion herein is not complete but should as least give the reader some
idea of what is going on.
2. Pseudofile I/O
While a task is running the CL will be reading command input from the task.
This read eventually resolves into a call to PR_PSIO on the CLIN for the
process. When pseudofile i/o occurs, e.g., the process writes to STDOUT or
STDERR, an XMIT or XFER directive will be seen in the CLIN input from the
process. If we are directed to XMIT to an ordinary file our task is relatively
easy, i.e., we read the data block from CLIN and write it to the output file.
A directive to read from the standard input is also easy, i.e., we read from
the standard input of the parent (assuming i/o is not redirected) and write
the data block to the CLOUT of the process preceded by a count of the number
of chars.
2.1 I/O to a Graphics Stream
If we are directed to read or write a graphics stream our task is somewhat
more difficult. The standard graphics streams STDGRAPH, STDIMAGE, and STDPLOT
differ from other pseudofiles in that the streams are both readable and
writable, provided all data is used up before switching modes. A graphics
stream may be connected to a file if output is being spooled, to the builtin
STDGRAPH kernel if the graphics device is the graphics terminal, or to a
graphics kernel resident in an external subprocess.
If a graphics stream is redirected to a spool file we merely copy output to
the file and reading is forbidden. If output is to an external graphics
kernel but is unfiltered (no workstation transformation, e.g., for STDPLOT),
we merely copy data blocks on to the subprocess but the protocol involved
is nontrivial. If output is to the builtin STDGRAPH kernel or to an
external interactive kernel, output must be filtered through GIOTR before
being written to the local or remote graphics kernel. Graphics input is
also possible and is handled similarly but without need to call GIOTR.
Before reading or writing a graphics stream GIO will send a special directive to
PR_PSIO to connect a kernel to the stream. This directive is passed to PR_PSIO
via an XMIT to the special pseudofile PSIOCONTROL. The data passed in the
XMIT call will be the GKI control instruction to be executed by PSIO. There
are currently three such directives, i.e., OPENWS, SETWCS, and GETWCS. Each
such directive is included in the normal metacode stream as well, but by
writing to a special pseudofile we avoid the need to have PR_PSIO scan each
metacode stream for control instructions, a fairly expensive operation if a
lot of data is involved.
2.1.1 Graphics Stream Dataflow
A frame buffer is associated with each graphics stream in the parent
process. If graphics output (metacode) is being filtered, each output record
is appended to the frame buffer and then GIOTR is called to filter the new
instructions. GIOTR writes the filtered metacode stream either directly to
the builtin kernel or to the graphics output pseudofile stream of the parent
process.
Output to the builtin kernel is easy to understand: GIOTR merely calls the
kernel to execute the transformed instruction. If output is to an external
kernel we unfortunately cannot simply write to the kernel because we require
that the graphics kernel task be a conventional task callable from either
the CL or by the graphics system, i.e., by PL_PSIO. We must buffer the
transformed output metacode and pass it on to the kernel process only when
requested to do so by an XFER command from the kernel.
This buffering is done in a somewhat tricky way which makes it look like we
are writing to a simple file, and which allows us to use conventional READ and
WRITE calls to access the graphics stream. GIOTR, if not writing to the
builtin kernel, will write to one of the three graphics streams of the parent
process, i.e., to STDGRAPH, STDIMAGE, or STDPLOT. The graphics stream of the
parent is logically connected to the same stream in the kernel process. We
arrange things such that data may be written or read into the FIO buffer
associated with the stream, but the buffer will never actually be flushed,
since this would cause the contents to appear as garbage on the user terminal.
The sequence of events for an XMIT to STDGRAPH with an external kernel is as
follows:
The parent process (CL) blocks, waiting for a read on the IPC
channel to the graphics task.
Graphics task writes to stdgraph.
FIO flushes stdgraph buffer through IPC channel.
PR_PSIO (in the parent) sees XMIT to stdgraph.
Parent reads data record from IPC channel, appending the
data record to the frame buffer for the stream.
PR_PSIO calls GIOTR to process the new metacode.
GIOTR writes the transformed metacode instructions to the stdgraph
stream of the parent and returns control to PR_PSIO.
PR_PSIO rewinds the stdgraph buffer in preparation for a read and
stacks the pending XMIT request and directs its command input
to the IPC of the kernel process.
The kernel process sends zero or more XMIT or XFER requests to
the parent to read or write pseudofile streams other than
stdgraph.
The kernel process sends an XFER request to the parent to read
from stdgraph.
The parent reads the data record from the stdgraph FIO buffer
and passes it on to the kernel, completing the XFER request
of the kernel as well as the original XMIT request of the
graphics task.
The parent process (CL) blocks, waiting for a read on the IPC
channel to the graphics task.
The sequence of operations for an XFER request from the graphics task is
as follows.
The parent process (CL) blocks, waiting for a read on the IPC
channel to the graphics task.
The parent receives an XFER request from the graphics task.
If there is any data in the stdgraph buffer the parent returns
that to the graphics task, otherwise the PR_PSIO procedure
pushes an XFER request and redirects its input to the
graphics kernel.
The kernel process sends zero or more XMIT or XFER requests to
the parent to read or write pseudofile streams other than
stdgraph.
The kernel process sends an XMIT request to the parent to write
to stdgraph.
The parent reads the data block from the IPC channel to the kernel
and writes it to stdgraph, completing the XMIT request.
The parent pops the XFER request and copies the data in the stdgraph
buffer to the graphics task, completing the original XFER request.
The parent process (CL) blocks, waiting for a read on the IPC
channel to the graphics task.
In summary, the principal data buffers involved in pseudofile i/o to a graphics
stream are the frame buffer, used by GIOTR to spool the metacode instructions
for a graphics frame, and the FIO buffer for the graphics stream, used to
pass data between XMIT/XFER request pairs from cooperating processes at
opposite ends of a graphics stream.
3. Summary
The actual code required to implement all this is probably easier to
understand than the English description. To summarize the justification for
the complexity of the scheme we have adopted:
[1] The graphics kernel task is a conventional CL callable task with
parameters etc., usable to process metacode from a metafile or from
a pipe as well as callable by PR_PSIO. The conventional IPC protocol
is used in the graphics kernel task. Other tasks may be resident in
the same process, saving disk and memory.
[2] The graphics kernel may read STDIN and write STDOUT and STDERR while
processing metacode, allowing access to the graphics terminal via the
CL process, output of debugging information during operation, and
output of error messages during operation.
.endhelp ______________________________________________________________________
# PR_PSIO -- Process an i/o request for the specified pseudofile stream
# of the specified process.
procedure pr_psio (pid, active_fd)
pid process id
fd process stream for which i/o is requested
begin
in = pr.clin
fd = active_fd
clear stack
# Process i/o requests from the subprocess until a request is received
# and processed for pseudofile FD.
repeat {
while (filbuf (in) != EOF) {
determine type of request and destfd
if (xmit request to stream destfd) {
if (graphics filtering enabled) {
read data record and append to frame buffer
call giotr to filter output to destfd
} else {
read data record from process
write record to destfd
}
if (destination is a process) {
rewind destfd buffer for read
push (fd)
push (in)
push (xmit)
fd = destfd
in = newpr.clin
next
}
} else if (xfer request from stream destfd) {
if (destfd is a process and buffer is empty) {
push (fd)
push (in)
push (xfer)
fd = destfd
in = newpr.clin
next
} else {
read data record from destfd
write data record to process
}
} else if (gio directive) {
if (open workstation)
connect a kernel process to a graphics stream
else if (setwcs)
save wcs for the stream
else if (getwcs)
write wcs data to the process
} else {
destfd = CLIN
if (fd != CLIN)
error: unsolicited command input from the subprocess
}
if (destfd == fd) {
if (stack not empty) {
pop (request)
pop (in)
pop (fd)
if (request == xfer) {
read data record from fd
write data record to process owning "in"
}
} else
break
}
}
} until (stack is empty)
end
File routing:
Each pseudofile in a subprocess is associated with a stream in the parent
process. A subprocess pseudofile may map to a real file or to a parent
pseudofile. When a subprocess is connected as a graphics kernel graphics
i/o will be via any one of the standard graphics streams STDGRAPH etc.,
with said graphics stream connected to the same stream in the parent.
The subprocess streams STDIN, STDOUT, and STDERR are by default connected
to the same streams in the parent, allowing the subprocess to access the
terminal, output error messages, and so on. A graphics kernel will be able
to access the standard i/o streams even while connected as a subprocess
to filter GKI metacode.
|