是时候有一个像样的打印工具了,在kernel\chr_drv目录下添加tty_io.c,将tty_table的定义,copy_to_cooked和do_tty_interrupt都移到这个文件中。
添加get_fs_byte函数是读取fs段内addr地址处(fs:[addr])一字节的数据,%0是返回的 寄存器变量_v;%1是内存地址addr,定义一个寄存器变量_v,该变量将被保存在一个寄存器中,以便提高访问和操作效率。
函数的主要作用是读取buf中的数据并put进tty->write_q队列,然后调用tty->write(tty)输出。
#include <linux/head.h> #include <linux/sched.h> #include <linux/tty.h> #include <asm/system.h> struct tty_struct tty_table[] = { { con_write, {0,0,0,0,""}, /* console read-queue */ {0,0,0,0,""}, /* console write-queue */ {0,0,0,0,""} /* console secondary queue */ } }; void copy_to_cooked(struct tty_struct * tty) { signed char c; if(!EMPTY(tty->read_q) ){ GETCH(tty->read_q,c); PUTCH(c,tty->write_q); tty->write(tty); } } void do_tty_interrupt(int tty) { copy_to_cooked(tty_table+tty); } inline unsigned char get_fs_byte(const char * addr) { unsigned register char _v; __asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr)); return _v; } int tty_write(unsigned channel, char * buf, int nr) { static cr_flag=0; struct tty_struct * tty; char c, *b=buf; if (channel>2 || nr<0) return -1; tty = channel + tty_table; while(nr>0 && !FULL(tty->write_q)){ c=get_fs_byte(b); if(c=='\n') c=13; b++; nr--; PUTCH(c,tty->write_q); } tty->write(tty); return (b-buf); }
头文件tty.h中添加
#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1)) #define FULL(a) (!LEFT(a)) void con_write(struct tty_struct * tty);
#ifndef STDARG_H #define STDARG_H typedef char *va_list; #endif /* STDARG_H */
%4是base,eax中存放的是n,所以divl %4相当于%eax = n/base; %edx = n%base;
输出寄存器列表:"=a" (n),"=d" (__res),说明代码运行结束后将eax的值放到n中,将%ebx中的值放到__res中。
最后一行的__res;说明该表达式的输出值为__res
所以n中保存商,返回值为余数
C语言里规定:16bit程序中,返回值保存在ax寄存器中,32bit程序中,返回值保持在eax寄存器中,如果是64bit返回值,edx寄存器保存高32bit,eax寄存器保存低32bit
number是将整数num转换为相应的16进制数的字符串
#include <stdarg.h> #define do_div(n,base) ({ \ int __res; \ __asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \ __res; }) static char * number(char *str, int num, int base) { char c,sign,tmp[36]; int i=0; const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (num==0) tmp[i++]='0'; else while (num!=0) tmp[i++]=digits[do_div(num,base)]; while(i-->0) *str++ = tmp[i]; return str; } typedef char *va_list; int vsprintf(char *buf, const char *fmt, va_list args) { int len; int i; char * str; for (str=buf ; *fmt ; ++fmt) { if (*fmt != '%') { *str++ = *fmt; continue; } *fmt++; switch (*fmt) { case 'x': //flags |= SMALL; case 'X': str=number(str,*((int *)args), 16); break; case 'd': case 'i': //flags |= SIGN; case 'u': str = number(str,*((int *)args), 10); break; } } *str = '\0'; return str-buf; }
在kernel文件夹下添加printk.c
C调用约定:函数后面的参数先入栈,并且由调用者清理堆栈。所以fmt最后入栈,&fmt+4为fmt后面一个参数的地址 。。。。。
#include <linux/kernel.h> #include <stdarg.h> static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args); int printk(const char *fmt, ...) { int i; va_list args = (va_list)((char*)(&fmt)+4); i=vsprintf(buf,fmt,args); __asm__("push %%fs\n\t" "push %%ds\n\t" "pop %%fs\n\t" "pushl %0\n\t" "pushl $buf\n\t" "pushl $0\n\t" "call tty_write\n\t" "addl $8,%%esp\n\t" "popl %0\n\t" "pop %%fs" ::"r" (i):"ax","cx","dx"); return i; }
# Makefile for the simple example kernel. AS =as LD =ld LDFLAGS = --oformat binary -N -e start -Ttext 0x0 CC =gcc CFLAGS = -I../include -fno-stack-protector .c.s: $(CC) $(CFLAGS) \ -S -o $*.s $< .s.o: $(AS) -c -o $*.o $< .c.o: $(CC) $(CFLAGS) \ -c -o $*.o $< OBJS = asm.o kliba.o System_call.o chr_drv/chr_drv.o Traps.o sched.o printk.o vsprintf.o kernel.o:$(OBJS) $(LD) -r -o kernel.o $(OBJS) sync chr_drv/chr_drv.o: (cd chr_drv; make) clean: rm -f *.o *.s (cd chr_drv;make clean)