--------------------------------------------------------
printf("_")
--------------------------------------------------------
注意我们的 printf 必须实现为系统调用,因为用户进程没有操作显存的权限,必须通过系统调用跳到高特权级之后由内核来替进程完成任务!
我们的思路是这样的,假设有三个进程,我们将进程绑定到 TTY 上,让进程在 TTY 上运行,言外之意就是进程产生的所有输出都将输出到进程所在的 TTY 上(也就是 TTY 所在的控制台上),具体怎么实现呢?进程 PROCESS 结构体有个 nr_tty 成员,指定进程绑定的 TTY 号,这里重点在于 printf 的实现,因为是进程使用 printf ,那么 printf 的输出就必须在调用它的进程所在的 console 上
——printf 运行的时候必须知道自己是被哪个进程调用的,然后根据此进程的 nr_tty 获得此进程基于的 TTY,最后将字符输出到这个 TTY 对应的 console 上!
——当一个进程在运行期间,那么全局变量 p_proc_ready 就指向此进程的 PROCESS 结构,所以进程只要在调用 printf 的时候将 p_proc_ready 传递给 printf ,就能标识是以他的身份调用 printf!
PROCESS 结构体:
typedef struct s_proc {
STACK_FRAME regs; /* process' registers saved in stack frame */
t_16 ldt_sel; /* selector in gdt giving ldt base and limit*/
DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
int ticks; /* remained ticks */
int priority;
t_32 pid; /* process id passed in from MM */
char name[16]; /* name of the process */
int nr_tty;
}PROCESS;
// ----------------------------
// <printf.c>
// Jack Zheng 12.2
// ----------------------------
#include "type.h"
#include "const.h"
int printf(const char *fmt, ...)
{
int i;
char buf[256];
va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
// ----------------------------
// <vsprintf.c>
// Jack Zheng 12.2
// ----------------------------
#include "type.h"
#include "const.h"
#include "string.h"
int vsprintf(char *buf, const char *fmt, va_list args)
{
char* p;
char tmp[256];
va_list p_next_arg = args;
for (p=buf;*fmt;fmt++) {
if (*fmt != '%') {
*p++ = *fmt;
continue;
}
fmt++;
switch (*fmt) {
case 'x':
itoa(tmp, *((int*)p_next_arg));
strcpy(p, tmp);
p_next_arg += 4;
p += strlen(tmp);
break;
case 's':
break;
default:
break;
}
}
return (p - buf);
}
; ----------------------------
; <syscall.asm>
; Jack Zheng 11.28
; ----------------------------
%include "sconst.inc"
INT_VECTOR_SYS_CALL equ 0x90
_NR_get_ticks equ 0
_NR_write equ 1
global get_ticks
global write
bits 32
[section .text]
; ----------------------------
; int get_ticks();
; ----------------------------
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
; ----------------------------
; ----------------------------
; void write(char* buf, int len);
; ----------------------------
write:
mov eax, _NR_write
; can't pass params by stack, because stack will be different after call 'INT'
mov ebx, [esp + 4] ; buf
mov ecx, [esp + 8] ; len
int INT_VECTOR_SYS_CALL
ret
; ----------------------------
// ----------------------------
// <tty.c>
// Jack Zheng 11.30
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#define TTY_FIRST (tty_table)
#define TTY_END (tty_table + NR_CONSOLES)
/* 本文件内函数声明 */
PRIVATE void init_tty(TTY* p_tty);
PRIVATE void tty_do_read(TTY* p_tty);
PRIVATE void tty_do_write(TTY* p_tty);
PRIVATE void put_key(TTY* p_tty, t_32 key);
PUBLIC void task_tty()
{
TTY* p_tty;
init_keyboard();
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
init_tty(p_tty);
}
// active tty0 in begining!
select_console(0);
while (1) {
for (p_tty=TTY_FIRST;p_tty<TTY_END;p_tty++) {
tty_do_read(p_tty);
tty_do_write(p_tty);
}
}
}
PRIVATE void init_tty(TTY* p_tty)
{
p_tty->inbuf_count = 0;
p_tty->p_inbuf_head = p_tty->p_inbuf_tail = p_tty->in_buf;
init_screen(p_tty);
}
PUBLIC void in_process(TTY* p_tty, t_32 key)
{
if (!(key & FLAG_EXT)) {
put_key(p_tty, key);
}
else {
int raw_code = key & MASK_RAW;
switch(raw_code) {
case ENTER:
put_key(p_tty, '\n');
break;
case BACKSPACE:
put_key(p_tty, '\b');
break;
case UP:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) { /* Shift + Up */
scroll_screen(p_tty->p_console, SCROLL_SCREEN_UP);
}
break;
case DOWN:
if ((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) { /* Shift + Down */
scroll_screen(p_tty->p_console, SCROLL_SCREEN_DOWN);
}
break;
case F1:
case F2:
case F3:
case F4:
case F5:
case F6:
case F7:
case F8:
case F9:
case F10:
case F11:
case F12:
if ((key & FLAG_ALT_L) || (key & FLAG_ALT_R)) { /* Alt + F1~F12 */
select_console(raw_code - F1);
}
break;
default:
break;
}
}
}
PRIVATE void put_key(TTY* p_tty, t_32 key)
{
if (p_tty->inbuf_count < TTY_IN_BYTES) {
*(p_tty->p_inbuf_head) = key;
p_tty->p_inbuf_head++;
if (p_tty->p_inbuf_head == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_head = p_tty->in_buf;
}
p_tty->inbuf_count++;
}
}
PRIVATE void tty_do_read(TTY* p_tty)
{
if (is_current_console(p_tty->p_console)) {
keyboard_read(p_tty);
}
}
PRIVATE void tty_do_write(TTY* p_tty)
{
if (p_tty->inbuf_count) {
char ch = *(p_tty->p_inbuf_tail);
p_tty->p_inbuf_tail++;
if (p_tty->p_inbuf_tail == p_tty->in_buf + TTY_IN_BYTES) {
p_tty->p_inbuf_tail = p_tty->in_buf;
}
p_tty->inbuf_count--;
out_char(p_tty->p_console, ch);
}
}
PUBLIC void tty_write(TTY* p_tty, char* buf, int len){
char* p = buf;
int i = len;
while(i){
out_char(p_tty->p_console, *p++);
i--;
}
}
PUBLIC int sys_write(char* buf, int len, PROCESS* p_proc){
tty_write(&tty_table[p_proc->nr_tty], buf, len);
return 0;
}
·kernel.asm 节选
; ----------------------------
; sys_call
; ----------------------------
sys_call:
call save
; ---
push dword [p_proc_ready] ; 将 p_proc_ready 指针的值入栈
push ecx
push ebx
; ---
sti
call [sys_call_table + eax * 4]
add esp, 4 * 3
mov [esi + EAXREG - P_STACKBASE], eax ; 返回值
cli
ret
; ----------------------------
运行:
OK,我们看到 printf 不只是打印字符那么简单,它还牵连出了进程的输出要到进程所在的 TTY 上!
PS:毫无疑问这节又是架构的问题(操作系统的涉及充满了架构了问题...)!