实验21.实现 printf

已完成实验

已完成实验链接

简介

实验 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.rhkb.cn/news/388547.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于N32L406MB EasyFlash参数(key-value)记录库移植

EasyFlash 感谢作者的分享https://github.com/armink/EasyFlash EasyFlash是一款开源的轻量级嵌入式Flash存储器库&#xff0c;方便开发者更加轻松的实现基于Flash存储器的常见应用开发 三大实用功能 ENV快速保存产品参数(key-value)&#xff0c;支持 写平衡&#xff08;磨…

最小例程上加OLED显示

最小例程上加OLED显示 本工程代码链接: https://ww0.lanzoul.com/i8lNa265gj7g 失效联系:qq2958360390 我们其实就加上这几个文件, 然后会调用就可以了, 具体的就看江协科技的OLED, 讲的很清楚, 我们这里只说应用, 我们的重点在使用. 下面跟着我来, 复制黏贴: 更详细请看哔哩…

从零开始学习机器学习,掌握AI未来的关键!

从零开始学习机器学习 1. 介绍1.1 人工智能&#xff08;AI&#xff09;概述1.2 机器学习在人工智能中的应用1.3 机器学习基础概念 2. 监督学习2.1 什么是监督学习2.2 回归分析2.3 分类问题2.4 模型评估和选择 3. 无监督学习3.1 什么是无监督学习3.2 聚类算法3.3 降维技术 4. 深…

(39)智能电池

文章目录 前言 1 通过任务规划器进行设置 2 补充信息 3 限制条件 4 参数说明 前言 虽然还不是很普遍&#xff0c;但智能电池更容易从飞行器上安装和拆卸&#xff0c;并且能够提供更多关于电池状态的信息&#xff0c;包括容量、单个电池电压、温度等。 ArduPilot 支持几种…

qt的信号槽连接成功,但是就是无法触发槽函数。你必须使用connect的第5个参数,Qt::QueuedConnection

signals:void sends(); public slots:void sl();//这种是默认自动连接&#xff0c;故第五个参数不用写connect(this,&MainWindow::sends,this,&MainWindow::sl);emit sends();void sl() {}如果connect连接成功&#xff0c;但是无法触发槽函数。你应该使用第五个参数&…

【vue-cli】vue-cli@2源码学习

vue-cli 2 源码 @vue/cli: 3.11.0创建项目 vue create 项目名称 @vue/cli: 2.x.x 创建项目 vue init webpack yhh-project 脚手架初始化项目流程: 下载vue/cli@2 源码 下载完成后初始化 npm i 创建项目 vue init webpack yhh-project vue-init: bin/vue-init #!/usr/bin/e…

与Zoom集成获取会议开始和结束事件

一、注册一个Zoom免费帐号&#xff08;需要在国外注册&#xff0c;国内不允许&#xff09; 二、进入Zoom应用市场创建一个应用 点击”发展”&#xff08;开发&#xff09;菜单&#xff0c;选择构建应用。 同意条款&#xff1a; 选择应用类型&#xff1a; 设置应用信息&#x…

Linux进程控制——进程程序替换、bash的模拟实现

文章目录 exec系列函数execlexeclp和execle execv系列函数bash的模拟实现实现思路完整代码其他问题 在学习进程的时候&#xff0c;我们想fork一个子进程&#xff0c;然后就可以给他布置任务了 但是如果我们分成两个人开发&#xff0c;父子进程分别负责不同的任务&#xff0c;等…

编程小白如何成为大神?大学新生的最佳入门攻略

目录 方向一&#xff1a;选择适合的编程语言 方向二&#xff1a;制定有效的学习计划 方向三&#xff1a;避免常见的学习陷阱 方向四&#xff1a;额外建议 编程已成为当代大学生的必备技能&#xff0c;但面对众多编程语言和学习资源&#xff0c;新生们常常感到迷茫。如何选择…

深度学习模型服务端部署——flask+gunicorn+supervisor+nginx+docker

前言&#xff1a;深度学习模型经过前期的训练调优评估&#xff0c;最终得到一个精度速度满足要求的模型(.pth, .ckpt&#xff0c;或者.onnx等等格式)&#xff0c;但模型要实际用起来&#xff0c;还得部署起来&#xff0c;部署分为在移动端芯片上和服务器上。在移动端芯片部署通…

大龄程序员转型攻略:拥抱人工智能,开启新征程

前言 随着科技的飞速发展&#xff0c;人工智能浪潮席卷全球&#xff0c;相关岗位炙手可热。在这个背景下&#xff0c;许多大龄程序员开始思考如何转型&#xff0c;以适应时代的变化。结合自身编程基础&#xff0c;大龄程序员可以学习机器学习、深度学习算法&#xff0c;投身于…

蓝桥杯 Python 研究生组-2023-省赛-工作时长

蓝桥账户中心https://www.lanqiao.cn/problems/3494/learning/ 问题描述 小蓝手里有一份 20222022 年度自己的上班打卡记录文件&#xff0c;文件包含若干条打卡记录&#xff0c;每条记录的格式均为“yyyy-MM-dd HH:mm:ssyyyy-MM-dd HH:mm:ss”&#xff0c;即按照年-月-日 时:…

转录组数据去批次方法整理(combat,combat-seq,removeBatchEffect)

为什么需要去除批次效应&#xff1f; 批次效应是指样本在不同批次处理及测量的情况下引入与生物学情况不相关的技术误差&#xff0c;比如不同试剂耗材&#xff0c;不同操作者&#xff0c;不同的实验时间等。 正是因为这些非生物学的因素存在就有可能会导致我们的结果偏离真实…

网络通信---UDP

前两天做了个mplayer项目&#xff0c;今日继续学习 网络内容十分重要&#xff01;&#xff01;&#xff01; 1.OSI七层模型 应用层:要传输的数据信息&#xff0c;如文件传输&#xff0c;电子邮件等&#xff08;最接近用户&#xff0c;看传输的内容类型到底是什么&#xff09; …

精进日常:每日练习与明智取舍的艺术

目录 题目1.对于非运行时异常&#xff0c;程序中一般可不做处理&#xff0c;由java虚拟机自动进行处理。2.下面哪个关键字可以用于Java的构造方法上&#xff1f;3.以下代码执行的结果显示是多少&#xff08; &#xff09;&#xff1f;注解总结 题目 选自牛客网 1.对于非运行时…

[工具推荐]前端加解密之Burp插件Galaxy

如果觉得该文章有帮助的&#xff0c;麻烦师傅们可以搜索下微信公众号&#xff1a;良月安全。点个关注&#xff0c;感谢师傅们的支持。 免责声明 本号所发布的所有内容&#xff0c;包括但不限于信息、工具、项目以及文章&#xff0c;均旨在提供学习与研究之用。所有工具安全性…

前后端demo-WarehouseManagement

前端 数据库 其他 1.git下来&#xff0c;解决依赖问题&#xff0c;前端报错因为字体文件丢失&#xff0c;下载字体放到fonts文件夹字体.zip官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 2.后端login验证&#xff0c;前端需要账号格式&#xff0c;linqq.com 3.自己…

国产麒麟操作系统下搞单机版

去年纪委单位的一个项目&#xff0c;因为单位保密性质&#xff0c;档案必须要保密&#xff0c;要求采用单机版&#xff0c; 要求跟EXE那样&#xff0c;双击打开&#xff0c;阿公单位信息人员电脑操作水平化滞后还是相当严重啊。 去年已经给他花了时间按他们的要求实现了。 上周…

嵌入式C++、ROS 、OpenCV、SLAM 算法和路径规划算法:自主导航的移动机器人流程设计(代码示例)

在当今科技迅速发展的背景下&#xff0c;嵌入式自主移动机器人以其广泛的应用前景和技术挑战吸引了越来越多的研究者和开发者。本文将详细介绍一个嵌入式自主移动机器人项目&#xff0c;涵盖其硬件与软件系统设计、代码实现及项目总结&#xff0c;并提供相关参考文献。 项目概…

Day14-Servlet后端验证码的实现

图片验证码的生成采用的是Kaptcha&#xff1b; Kaptcha是一个高度可配置的验证码生成工具&#xff0c;由Google开源。它通过一系列配置文件和插件&#xff0c;实现了将验证码字符串自动转换成图片流&#xff0c;并可以与session进行关联&#xff0c;从而在验证过程中使用&#…