************
Часть третья
************
Issues in exploring <stdio.h> stream access.
1.
bugfixes
When we does fseek() if the new file pointer points to data that is already in the buffer, nothing need to be done by IO system calls.
by stdio convention this is not true, fseek() will always reset FILE* buffer, only serial 'getc/putc' calls will skip IO system calls.
the convention is taken by stdio designers to simplify coherent FILE* access by the price of fseek() effectivity.
1.2
>"assert(??? != 01L)"
means "-01L"
1.3
>in function fdup_r(), "//drop dfcr"
never can "//drop dfcr" due to possible fdopen errors (now the function name is changed into fdup_pipe_r())
2.
Some summary info.
conceptually there are two parts of file handle: "system" and "per_process"
"system" has "per_process" references_list accessed by [process pid]
references_list item {
long owner_pid;
ulong num_of_the_process_dups;
};
references_list item will be removed if owner_pid terminated or no more `num_of_the_process_dups`
system handle will be closed if no more references_list "items"
system{
ulong lseek_pos;
references_list per_process[];
ubits system_io_restrictions;
}
process{
long system_fildes;
ubits per_process_io_restrictions;
}
2.1
there are several ways to create file handle that involves different parts of file handle:
IO call system per_process
open + + check access (chown,chmod) and create new both parts
fork - + dup "per_process" part for new proccess with the same io_restrictions
dup - + dup "per_process" part for current proccess with the same or more strict io_restrictions
dopen + + dup both parts with the same or more strict io_restrictions (is not supported by posix/stdlib)
close ? + close "per_process" part, close "system" part if no more "per_process" refs
2.2
we consider lseek_pos created as common (in "system part" of file handle) in order to simplify "read/write" calls to file handle interleaved by different processes without IPC locks usage, from first look there are no ways to get the same behaviour by private lseek_pos in "per_process" part.
coherent by process pair { lseek, read/write } requires IPC lock usage in IO access interleaved by different processes
dup does for file handle the similar as fork, but for current process
2.3
"fildes dopen(fildes, io_restriction)" works similar to "FILE* fdopen(fildes, io_restriction)"
the same as fdopen() or dup(), dopen() does not check access (chown,chmod) and uses existed system_io_restrictions created by open(). io_restriction is open() attr "O_RW/O_RO/O_WO".
dopen is not supported by posix/stdlib.
dopen() works with regular files and pipes.
for pipes file handle has no lseek() interface (lseek() returns ESPIPE), so for pipes ordinary dup() works very closely to dopen()
2.4
the our (not from lib) function fdup_pipe_r() is intended for bi_direction "r+" pipes only, to dup() from original bi_direction "r+"(O_RW) FILE* stream (created by open()/fopen()) new separated uni_direction "r"(O_RO) FILE* stream, in order to use:
— "r+" FILE* stream to uni_direction any buffered, pipe only write
— "r" FILE* stream to uni_direction any buffered, pipe only read
the function fdup_pipe_r() allows us to access guest UART 8250 port from host machine for virtualbox
3.
stdio FILE* dups
bi_direction "r+" streams
for bi_direction "r+" `FILE*` access, "pipe" is NOT base interface for any "regular file",
for interleaved r/w access in code is intended for "pipe" can not subst any "regular file",
the interleaving r/w for "regular file" requires `fseek()/ftell()`
for bi_direction "r+" `FILE*` access, "pipe" can not be buffered,
it is required to call `setvbuf(_IONBF)` for the "pipe",
the interleaving r/w for the "pipe" requires `push_back()` input `FILE*` buffer
for bi_direction "r+" `FILE*` access, "pipe" should be splitted by `fdup_pipe_r()`
into two separated uni_direction "r"/"w" `FILE*` access
to improve interleaved r/w access in code is intended for "pipe"
uni_direction "r"/"w" streams
for uni_direction "r"/"w" `FILE*` access, "pipe" IS base interface for SOME "regular file",
for uni_direction r/w access in code is intended for "pipe" can subst the "regular file",
in which the data is placed in real one-by-one as pipes does
so "pipe" interface should be "defined up to complete" by two fake calls:
`rewind()` the same as `fseek(0,SEEK_SET)`
`append()` the same as `fseek(0,SEEK_END)`
for uni_direction "r"/"w" `FILE*` access (including "r+" pipes splitted by `fdup_pipe_r()`)
"pipe" can be buffered by any `_IO?BF` type (by `setvbuf()` call)
so "r+" pipes splitted by `fdup_pipe_r()` in the two uni_direction "r"/"w" derived pipes
povide any buffered interleaved r/w access in code is intended for "pipe"
(by the two uni_direction "r"/"w" derived pipes working together)
it is prohibited to create code is intended for "pipe" in uni_direction "r"/"w" FILE* access, in which can subst any (without one-by-one placed data) "regular file" by fake fseek(rand(),SEEK_SET) calls in the code
if code needs `fseek(rand(),SEEK_SET)` calls, the code is not intended for pipes,
the code follows "regular file" interface (by `fseek()` requests),
but "regular file" is not base interface for "pipe".
attempt to subst "pipe" instead of "regular file" interface violates "type checking"
The next improved version of initial example
example4
//
FILE
//return "r" mode dupped fr from original "r+"("w") mode fw
*fdup_pipe_r(
FILE *const fw
){
int
dfw, dfr;
//assume "fileno" returns the same "fildes" value that is stored in the FILE*
//("fileno" does not make "dup")
dfw= fileno(fw); assert(dfw != -01L);
dfr= dup(dfw); assert(dfr != -01L);
//fr
FILE
*fr;
fr= fdopen(dfr, "rb"); if(!fr)close(dfr);
return fr;
//drop dfw, dfr values
}
//
{
enum{ PR_SYN= 0x16U };
//pipe
//open pipe in "r+"("w") mode fcw
fcw= fopen("//./pipe/dos1","rb+"); assert(fcw);
//return "r" mode dupped fcr from original "r+"("w") mode fcw
fcr= fdup_pipe_r(fcw); assert(fcr);
//unbuffered pipe is always required for our AUX port protocol "per char" exchange
//but for splitted pipes any buffering type "_IO?BF" could be set
assert( !setvbuf(fcr, 0, _IONBF, 0) );
assert( !setvbuf(fcw, 0, _IONBF, 0) );
//"r+" pipe in interleaved r/w access never allows subst regular file instead of pipe
//("rewind()/append()" calls can not help)
//assert( !fseek(fcr,0,SEEK_SET) );
//assert( !fseek(fcw,0,SEEK_END) );
//our AUX port protocol local files
fi= fopen("./isw","rb"); assert(fi);
//"a"(create if not exist) + reopen to "r+" + append()
fo= fopen("./osw","ab+"); assert(fo); fclose(fo);
fo= fopen("./osw","rb+"); assert(fo); assert( !fseek(fo,0,SEEK_END) );
//our AUX port protocol exchange loop
//terminates by ctrl+C
for(;;){
//rx
int
rx_ch= fgetc(fcr);
//"assert( feof(fcr) )" throw error if fgetc(fcr) returned EOF but !feof(fcr) (but better check errno)
//otherwise endless loop with intention
if(rx_ch == EOF){ assert( feof(fcr) ); break; }
if(rx_ch != PR_SYN)assert( fputc(rx_ch,fo) != EOF );
//tx
int
tx_ch= fi? fgetc(fi): PR_SYN;
//"assert( feof(fi) )" throw error if fgetc(fi) returned EOF but !feof(fi) (but better check errno)
//otherwise endless loop with intention
if(tx_ch == EOF){ assert( feof(fi) ); tx_ch= PR_SYN; }
assert( fputc(tx_ch,fcw) != EOF );
}}
Now the example looks better. guest UART 8250 port will be accessed by simple stdio read/write.