实验21.实现 printf

server/2024/10/11 6:48:17/

已完成实验

已完成实验链接

简介

实验 21. 实现 printf

总结

  • 简化系统调用和中断,用 eax 代表调用号参数,ebx,ecx,edx 来代表参数(syscall.c kernel.s)

  • 添加 write 的系统调用接口(syscall.c, syscall-init.c, print.s)

      注意:要更改 print.s 中清屏的地址
    
  • 添加 printf 函数接口

在这里插入图片描述

主要代码

syscall.h

在这里插入图片描述

syscall.c

// 文件: syscall.c
// 时间: 2024-08-01
// 来自: ccj
// 描述: 用户系统调用,把调用号和参数写入寄存器,执行int 0x80,从eax拿到返回值#include "syscall.h"/// 系统调用,进入0x80之前
// 1. 把调用号和参数写入寄存器
//      eax = NUMBER;
//      ebx = ARG1;
//      ecx = ARG2;
//      edx = ARG3;
// 2. int 0x80
// 3. retval = eax
#define _syscall0(NUMBER)                                                  \({                                                                     \int retval;                                                        \asm volatile("int $0x80" : "=a"(retval) : "a"(NUMBER) : "memory"); \retval;                                                            \})#define _syscall1(NUMBER, ARG1)                                                       \({                                                                                \int retval;                                                                   \asm volatile("int $0x80" : "=a"(retval) : "a"(NUMBER), "b"(ARG1) : "memory"); \retval;                                                                       \})#define _syscall2(NUMBER, ARG1, ARG2)                                                            \({                                                                                           \int retval;                                                                              \asm volatile("int $0x80" : "=a"(retval) : "a"(NUMBER), "b"(ARG1), "c"(ARG2) : "memory"); \retval;                                                                                  \})#define _syscall3(NUMBER, ARG1, ARG2, ARG3)                         \({                                                              \int retval;                                                 \asm volatile("int $0x80"                                    \: "=a"(retval)                                 \: "a"(NUMBER), "b"(ARG1), "c"(ARG2), "d"(ARG3) \: "memory");                                   \retval;                                                     \})/// @brief 返回当前任务pid
/// @return
uint32_t getpid() { return _syscall0(SYS_GETPID); }/// @brief 写入字符串
/// @param fd
/// @param buf 字符串
/// @param count 字符数量
/// @return
uint32_t write(char* str) { return _syscall1(SYS_WRITE, str); }

kernel.s

在这里插入图片描述

syscall-init.c

在这里插入图片描述

print.s

在这里插入图片描述

stdio.c

// 文件: stdio.c
// 时间: 2024-08-01
// 来自: ccj
// 描述: printf,%符号转义#include "stdio.h"
#include "interrupt.h"
#include "global.h"
#include "string.h"
#include "syscall.h"
#include "print.h"#define va_start(ap, v) ap = (va_list)(&v)
// v是字符串首地址
// &v就是字符串首地址的内存地址,也就是栈顶
// ap指向了栈顶#define va_arg(ap, t) *((t*)(ap += 4))  // ap指向下一个参数并返回其值
// t是 type 类型
#define va_end(ap)    ap = NULL  // 清除ap/* 将整型转换成字符(integer to ascii) */
static void itoa(uint32_t value, char** buf_ptr_addr, uint8_t base) {uint32_t m = value % base;  // 求模,最先掉下来的是最低位uint32_t i = value / base;  // 取整if (i) {                    // 如果倍数不为0则递归调用。itoa(i, buf_ptr_addr, base);}if (m < 10) {                             // 如果余数是0~9*((*buf_ptr_addr)++) = m + '0';       // 将数字0~9转换为字符'0'~'9'} else {                                  // 否则余数是A~F*((*buf_ptr_addr)++) = m - 10 + 'A';  // 将数字A~F转换为字符'A'~'F'}
}/* 将参数ap按照格式format输出到字符串str,并返回替换后str长度 */
uint32_t vsprintf(char* str, const char* format, va_list ap) {char* buf_ptr = str;const char* index_ptr = format;char index_char = *index_ptr;  // 格式字符串中的字符int32_t arg_int;char* arg_str;while (index_char) {if (index_char != '%') {  // 如果不是%,那么直接复制到str*(buf_ptr++) = index_char;index_char = *(++index_ptr);continue;}index_char = *(++index_ptr);  // index_char是&,那么得到%后面的字符做转换switch (index_char) {case 's':                         // %s处理arg_str = va_arg(ap, char*);  // 拿到字符串首地址strcpy(buf_ptr, arg_str);     // 复制到strbuf_ptr += strlen(arg_str);   // str指针增加index_char = *(++index_ptr);  // 拿到%之后的字符继续循环break;case 'c':*(buf_ptr++) = va_arg(ap, char);index_char = *(++index_ptr);break;case 'd':arg_int = va_arg(ap, int);/* 若是负数, 将其转为正数后,再正数前面输出个负号'-'. */if (arg_int < 0) {arg_int = 0 - arg_int;*buf_ptr++ = '-';}itoa(arg_int, &buf_ptr, 10);index_char = *(++index_ptr);break;case 'x':arg_int = va_arg(ap, int);itoa(arg_int, &buf_ptr, 16);index_char = *(++index_ptr);  // 跳过格式字符并更新index_charbreak;}}return strlen(str);
}/* 同printf不同的地方就是字符串不是写到终端,而是写到buf中 */
uint32_t sprintf(char* buf, const char* format, ...) {va_list args;uint32_t retval;va_start(args, format);retval = vsprintf(buf, format, args);va_end(args);return retval;
}/* 格式化输出字符串format */
uint32_t printf(const char* format, ...) {va_list args;char buf[1024] = {0};  // 用于存储拼接后的字符串va_start(args, format);  // 使args指向formatvsprintf(buf, format, args);va_end(args);return write(buf);
}

main.c

// 文件: main.c
// 时间: 2024-07-19
// 来自: ccj
// 描述: 内核从此处开始#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"// 两个内核线程
void k_thread_a(void*);
void k_thread_b(void*);
// 两个用户进程
void u_prog_a(void);
void u_prog_b(void);int main(void) {put_str("I am kernel\n");init_all();process_execute(u_prog_a, "user_prog_a");process_execute(u_prog_b, "user_prog_b");console_put_str("main_pid:0x");console_put_int(sys_getpid());console_put_char('\n');thread_start("k_thread_a", 31, k_thread_a, "argA ");thread_start("k_thread_b", 31, k_thread_b, "argB ");intr_enable();  // 打开中断,使时钟中断起作用while (1) {};return 0;
}// 内核线程函数
void k_thread_a(void* arg) {console_put_str("thread_a_pid:0x");console_put_int(sys_getpid());console_put_char('\n');while (1) {}
}
void k_thread_b(void* arg) {console_put_str("thread_b_pid:0x");console_put_int(sys_getpid());console_put_char('\n');while (1) {}
}// 测试用户进程
void u_prog_a(void) {printf("u_%s_pid:0x%d%c", "prog_a", getpid(), '\n');while (1) {}
}
void u_prog_b(void) {printf("u_%s_pid:0x%d%c", "prog_b", getpid(), '\n');while (1) {}
}

在这里插入图片描述


http://www.ppmy.cn/server/93526.html

相关文章

注册中心--Eureka

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;Spring Cloud实战&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1.项目问题 2.解决URL问题 2.1解决思路 2.2注册中心 2.3 CAP理…

【个人亲试最新】WSL2中的Ubuntu 22.04安装Docker

文章目录 Wsl2中的Ubuntu22.04安装Docker报错解决完全卸载Docker参考博客 &#x1f60a;点此到文末惊喜↩︎ Wsl2中的Ubuntu22.04安装Docker 友情提示&#xff1a;确定为wsl2ubuntu22.04&#xff0c;若按照以下步骤失败&#xff0c;请完全卸载后重装docker&#xff0c;并检查ws…

jdk1.8中HashMap为什么不直接用红黑树

最开始使用链表的时候&#xff0c;空间占用比较少&#xff0c;而且由于链表短&#xff0c;所以查询时间也没有太大的问题。可是当链表越来越长&#xff0c;需要用红黑树的形式来保证查询的效率。 参考资料&#xff1a; https://blog.51cto.com/u_13294304/3075723

抖音短视频矩阵管理系统:短视频运营的得力助手

1. 抖音短视频矩阵管理系统介绍 随着短视频行业的迅速发展&#xff0c;越来越多的企业和自媒体人开始关注短视频运营。抖音作为国内最受欢迎的短视频平台之一&#xff0c;拥有庞大的用户群体和丰富的内容资源。为了更好地管理和运营短视频&#xff0c;抖音短视频矩阵管理系统应…

uniapp手写滚动选择器

文章目录 效果展示HTML/Template部分&#xff1a;JavaScript部分&#xff1a;CSS部分&#xff1a;完整代码 没有符合项目要求的选择器 就手写了一个 效果展示 实现一个时间选择器的功能&#xff0c;可以选择小时和分钟&#xff1a; HTML/Template部分&#xff1a; <picker…

【算法】动态规划-斐波那契数列模型

目录 1、第N个泰波那契数 1.1 算法原理讲解 1.1.1 状态表示 1.1.2 状态转移方程 1.1.3 初始化 1.1.4 填表顺序 1.1.5 返回值 1.2 代码实现 1.3 空间优化 2、三步问题 2.1 算法原理讲解 2.1.1 状态表示 2.1.2 状态转移方程 2.1.3 初始化 2.1.4 填表顺序 2.1.5 返…

C端与B端 - 第一弹 - 理解和区分C端与B端软件开发

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有疑问和建议&#xff0c;请私信或评论留言&#xff01; 前言 在软件开发领域…

环境如何搭建部署Nacos

这里我使用的是Centos7&#xff0c; Nacos 依赖 Java环境来运行。如果您是从代码开始构建并运行Nacos&#xff0c;还需要为此配置 Maven环境&#xff0c;请确保是在以下版本环境中安装使用 ## 1、下载安装JDK wget https://download.oracle.com/java/17/latest/jdk-17_linux-x6…