xv6如何写入终端?

时间:2018-04-22 23:12:48

标签: stdout xv6

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的格式是什么?这是标准吗?

2 个答案:

答案 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