printf
函数调用write
(重新forktest.c):
void printf ( int fd, char *s, ... )
{
write( fd, s, strlen(s) );
}
将1
作为fd
写入控制台(1
映射到stdout)。但write
定义在哪里?我只在user.h中看到它的声明。
int write ( int, void*, int );
我假设它以某种方式被重定向到file.c中的filewrite
。
int filewrite (struct file *f, char *addr, int n )
{
int r;
if ( f->writable == 0 )
return -1;
if ( f->type == FD_PIPE )
return pipewrite( f->pipe, addr, n );
if ( f->type == FD_INODE )
{
// write a few blocks at a time to avoid exceeding
// the maximum log transaction size, including
// i-node, indirect block, allocation blocks,
// and 2 blocks of slop for non-aligned writes.
// this really belongs lower down, since writei()
// might be writing a device like the console.
int max = ( ( MAXOPBLOCKS - 1 - 1 - 2 ) / 2 ) * 512;
int i = 0;
while ( i < n )
{
int n1 = n - i;
if ( n1 > max )
n1 = max;
begin_op();
ilock( f->ip );
if ( ( r = writei( f->ip, addr + i, f->off, n1 ) ) > 0 )
f->off += r;
iunlock( f->ip );
end_op();
if ( r < 0 )
break;
if ( r != n1 )
panic( "short filewrite" );
i += r;
}
return i == n ? n : -1;
}
panic( "filewrite" );
}
并filewrite
调用fs.c中定义的writei
。
int writei ( struct inode *ip, char *src, uint off, uint n )
{
uint tot, m;
struct buf *bp;
if ( ip->type == T_DEV )
{
if ( ip->major < 0 || ip->major >= NDEV || !devsw[ ip->major ].write )
return -1;
return devsw[ ip->major ].write( ip, src, n );
}
if ( off > ip->size || off + n < off )
return -1;
if ( off + n > MAXFILE*BSIZE )
return -1;
for ( tot = 0; tot < n; tot += m, off += m, src += m )
{
bp = bread( ip->dev, bmap( ip, off/BSIZE ) );
m = min( n - tot, BSIZE - off%BSIZE );
memmove( bp->data + off%BSIZE, src, m );
log_write( bp );
brelse( bp );
}
if ( n > 0 && off > ip->size )
{
ip->size = off;
iupdate( ip );
}
return n;
}
这一切如何导致终端显示字符?终端如何知道读取fd 1进行显示,以及在哪里找到fd 1? fd 1的格式是什么?这是标准吗?
答案 0 :(得分:0)
fd==1
是指stdout
或标准输出。这是类Unix操作系统的常见功能。内核知道它不是真正的文件。写入stdout
的映射将映射到终端输出。
答案 1 :(得分:0)
以下是从$("body").scrollspy({
target: '.navbar',
offset: 10
});
到终端的完整路径。要点是,最终,xv6将字符写入CPU的串行端口。
QEMU使用标志printf
或-nographic
进行初始化,该标志指示QEMU使用终端将数据发送到CPU的串行端口或从CPU的串行端口接收数据。
步骤1) forktest.c中的-serial mon:stdio
printf
步骤2) usys.S中的void printf ( int fd, const char *s, ... )
{
write( fd, s, strlen( s ) );
}
void forktest ( void )
{
...
printf( 1, "fork test\n" );
...
}
write
步骤3) sysfile.c中的.globl write
write:
movl $SYS_write, %eax
int $T_SYSCALL
ret
sys_write
以前,在系统初始化期间,init.c中的int sys_write ( void )
{
...
argfd( 0, 0, &f )
...
return filewrite( f, p, n );
}
static int argfd ( int n, int *pfd, struct file **pf )
{
...
f = myproc()->ofile[ fd ]
...
}
被称为 stdin (0), stdout (1)和 stderr (2)创建文件描述符。这是main
在查找argfd
的文件描述符参数时所发现的。
sys_write
stdin | out | err是T_DEV类型的inode,因为它们是使用sysfile.c中的int main ( void )
{
...
if ( open( "console", O_RDWR ) < 0 )
{
mknod( "console", 1, 1 ); // stdin
open( "console", O_RDWR );
}
dup( 0 ); // stdout
dup( 0 ); // stderr
...
}
创建的
mknod
用于创建它们的主要设备编号int sys_mknod ( void )
{
...
ip = create( path, T_DEV, major, minor )
...
}
映射到控制台。参见file.h
1
步骤4) file.c中的// Table mapping major device number to device functions
struct devsw
{
int ( *read )( struct inode*, char*, int );
int ( *write )( struct inode*, char*, int );
};
extern struct devsw devsw [];
#define CONSOLE 1
filewrite
第5步) fs.c中的int filewrite ( struct file *f, char *addr, int n )
{
...
if ( f->type == FD_INODE )
{
...
writei( f->ip, addr + i, f->off, n1 )
...
}
...
}
writei
致电int writei ( struct inode *ip, char *src, uint off, uint n )
{
...
if ( ip->type == T_DEV )
{
...
return devsw[ ip->major ].write( ip, src, n );
}
...
}
成为devsw[ ip->major ].write( ip, src, n )
。
以前,在系统初始化期间,devsw[ CONSOLE ].write( ip, src, n )
将此映射到函数consoleinit
(请参见console.c)
consolewrite
步骤6) console.c中的void consoleinit ( void )
{
...
devsw[ CONSOLE ].write = consolewrite;
devsw[ CONSOLE ].read = consoleread;
...
}
consolewrite
步骤7) console.c中的int consolewrite ( struct inode *ip, char *buf, int n )
{
...
for ( i = 0; i < n; i += 1 )
{
consputc( buf[ i ] & 0xff );
}
...
}
consoleputc
步骤8) uart.c中的void consputc ( int c )
{
...
uartputc( c );
...
}
。
uartputc
汇编指令用于写入CPU的serial port。
out
步骤9) QEMU配置为使用串行端口通过#define COM1 0x3f8 // serial port
...
void uartputc ( int c )
{
...
outb( COM1 + 0, c );
}
或-nographic
标志在Makefile中进行通信。 QEMU使用终端将数据发送到串行端口,并显示来自串行端口的数据。
-serial mon:stdio