实验21.实现 printf

news2024/11/24 19:37:28

已完成实验

已完成实验链接

简介

实验 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);     // 复制到str
                buf_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_char
                break;
        }
    }
    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指向format
    vsprintf(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.coloradmin.cn/o/1967795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arbitrum Nitro交易速度压力测试实战:TPS性能评估全解析

Arbitrum Nitro 是一种基于以太坊的 Layer 2 扩展解决方案&#xff0c;旨在提高交易吞吐量并降低交易费用。为了全面评估其性能&#xff0c;我们需要进行了详细的压力测试。本文的目的是回顾一下我在实际测试过程中采用的方法&#xff0c;还有测试的思路。 我们的压力测试主要…

【Docker应用】快速搭建Plik服务结合内网穿透无公网IP远程访问传输文件

文章目录 前言1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 前言 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设…

页面路由怎么开发

首先删除它自带的页面 新建页面 Composition API 和 Options API 是 Vue.js 中两种不同的组件写法风格&#xff0c;它们用于定义 Vue 组件的结构和逻辑。我用的是Options API 配置路由 将它修改为需要的,按照上面的写法 如果component里面已经加了那么就不需要在上面加这是一种…

C++数据结构重要知识点(4)(map和set封装)

前面我们已经实现了红黑树&#xff0c;接下来我们需要将这个数据结构封装成map和set两个容器&#xff0c;其中很多地方的处理都有一定难度&#xff0c;下面会按照我的思路讲解map的改造 map成员变量如下&#xff0c;如果是第一次看到它&#xff0c;那么一定会很陌生&#xff0…

C高级(学习)2024.8.1

目录 shell命令 数组 数组的赋值 数组的调用 遍历数组 函数 函数的定义方式 函数调用 分文件编程 源文件 头文件 include引用时“”和<>的区别 编译工具 gcc编译工具 gdb调试 make工具 定义 Makefile格式 Makefile管理多个文件 Makefile变量 自定义…

发布NPM包详细流程

制作 首先需要制作一个npm包。 按照以下步骤依次执行。 mkdir my-npm-package cd my-npm-package npm init 相信这一步不需要过多的解释&#xff0c;就是创建了一个文件夹&#xff0c;然后初始化了一下文件夹。 然后在生成的package.json文件夹中更改一下自己的配置&…

Python-docx,修改word编辑时间总计、创建时间、上次修改时间、作者、上次修改者、备注

Python版本3.9&#xff0c;Python-docx版本1.1.2 修改下图中红框内的信息 创建时间、上次修改时间、作者、上次修改者、备注&#xff0c;这些都有接口&#xff0c;调用 import docx from docx import Document from docx.oxml.ns import qn from docx.shared import Inches, …

“2028年互联网上所有高质量文本数据将被使用完毕”

研究公司Epoch AI预测&#xff0c;到2028年互联网上所有高质量的文本数据都将被使用完毕&#xff0c;机器学习数据集可能会在2026年前耗尽所有“高质量语言数据”。研究人员指出&#xff0c;用人工智能(AI)生成的数据集训练未来几代机器学习模型可能会导致“模型崩溃”&#xf…

助力外卖霸王餐系统运营 微客云近期更新汇总

全面助力霸王餐合作运营&#xff0c;给大家汇报下最近微客云更新的内容&#xff0c;说实话近期非常的忙&#xff0c;各种功能上线&#xff0c;各种市场部们反馈的需求&#xff0c;微客云霸王餐招商体系&#xff08;分站&#xff09;自年底上线到现在&#xff0c;不知已更新了多…

2024年8月初AI大赛盛宴来袭!7场赛事等你挑战,最高奖金高达1.4万!

本期为大家带来7场精彩的AI大赛&#xff0c;主要以AI绘画大赛为主打&#xff0c;涵盖1场视频大赛和1场大模型大赛。 其中&#xff0c;以下3场大赛不容错过&#xff0c;分别是“36氪AI PARTNER2024具身智能大会”、“2024年大学生AI艺术季”和“混元万物 LiblibAlx腾讯混元模型…

微型丝杆弯曲:工件精度下降的隐形杀手!

微型丝杆作为精密机械部件&#xff0c;‌其弯曲或变形会对使用它进行加工的工件产生直接影响。在机械加工中&#xff0c;微型丝杆弯曲是一个不容忽视的问题&#xff0c;它会对工件造成多方面的损害。 1、加工精度受损&#xff1a;弯曲会直接导致工具的实际运动轨迹与程序设计的…

从零开始学习网络安全渗透测试之基础入门篇——(五)WEB抓包技术HTTPS协议APP小程序PC应用WEB转发联动

HTTP/HTTPS抓包技术是一种用于捕获和分析网络流量的方法&#xff0c;它可以帮助开发者、测试人员和网络安全专家理解应用程序的网络行为、调试问题、分析性能和识别潜在的安全威胁。 一、抓包技术和工具 &#xff08;一&#xff09;Charles Charles 是一款流行的网络调试代…

leetcode刷题日记-括号生成

题目描述 题目解析 回溯的题目&#xff0c;不过这个两个if我就感觉有点难以理解了&#xff0c;不过仔细的思考了一下&#xff0c;确实考虑到了每个位置的情况&#xff0c;特别是针对右边括号 题目代码 class Solution:def generateParenthesis(self, n: int) -> List[str…

苹果的秘密武器:折叠屏iPhone即将来袭,可能是史上最薄折叠屏?

苹果公司一直以来以其独特的产品设计理念和卓越的技术创新能力而闻名。近期&#xff0c;有关苹果折叠屏iPhone的消息再次引发了业界的高度关注。 据可靠消息源透露&#xff0c;这款备受期待的设备已经结束了实验阶段&#xff0c;预计最早将在2026年与消费者见面。 折叠屏iPhon…

【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…

大模型数据分析平台 LangSmith 介绍

[​LangSmith​]​ 是 LangChain 自主研发的 LLM 应用程序开发、监控和测试的平台。 LangChain 是一款使用 LLM 构建的首选开源框架&#xff0c;一个链接面向用户程序和 LLM 之间的一个中间层&#xff0c;允许 AI 开发者将像 ​​GPT-4​​ 、文心一言等大型语言模型与外部的计…

springboot依赖之JDBC API手写sql 管理数据库

JDBC API 依赖名称: JDBC API 功能描述: Database Connectivity API that defines how a client may connect and query a database. 数据库连接 API&#xff0c;定义客户端如何连接和查询数据库。 JDBC API 是 Java 标准库的一部分&#xff0c;提供低级别的数据库访问。需要…

基于SpringBoot+Vue的超市进销存系统(带1w+文档)

基于SpringBootVue的超市进销存系统(带1w文档) 基于SpringBootVue的超市进销存系统(带1w文档) 本系统提供给管理员对首页、个人中心、员工管理、客户管理、供应商管理、承运商管理、仓库信息管理、商品类别管理、 商品信息管理、采购信息管理、入库信息管理、出库信息管理、销售…

一次通过PMP考试的学习经验分享

很开心的收到PMI发来的邮件&#xff0c;祝贺我通过了PMP考试。应助教老师的邀请&#xff0c;简单说下我的一些学习备考经验&#xff0c;希望能给即将参加考试的大家带来一些收获。 第一&#xff0c;听基础课&#xff0c;截图做笔记 课程时间对我来说&#xff0c;还是蛮长的。…

MQA(Multi-Query Attention)详解

论文名称&#xff1a;Fast Transformer Decoding: One Write-Head is All You Need 论文地址&#xff1a;https://arxiv.org/abs/1911.02150v1 MQA(Multi-Query Attention)是Google团队在2019年提出的&#xff0c;是MHA (Multi-head Attention&#xff0c;多头注意力机制)的一…