首页 > 其他分享 >操作系统是如何执行应用软件的?

操作系统是如何执行应用软件的?

时间:2024-04-06 10:31:48浏览次数:26  
标签:rfs return 操作系统 应用软件 devp uint objnode 执行 buf

程序执行整体概览

在这里插入图片描述

案例

代码例子

#include "stdio.h"
int main(void)
{
	int i = 0;
	while (i < 1000) {
		printf("Hello World! %d\n", pid(NULL));
		i++;
	}

	return 0;
}

通过例子,探索printf()函数,一个文件库的使用流程

文件库

// printf.c
int printf(const char* fmt,...)
{
    int rets = -1;
    va_list ap; 
    va_start(ap,fmt);
	char* buf = (char*)mallocblk(0x1000);
	if(buf == NULL) {
        return -1;
	}

    devid_t dev;
    dev.dev_mtype = UART_DEVICE;
    dev.dev_stype = 0;
    dev.dev_nr = 0;
    hand_t fd = open(&dev, RW_FLG | FILE_TY_DEV, 0);
    if (fd == -1) {   
        rets=-1;
        goto res_step;
    }

    vsprintf(buf,fmt,ap);
    if (write(fd, buf, strlen(buf), 0) == SYSSTUSERR) {
       rets = -1;
       goto res_step;   
     }
     close(fd);
     rets = 0;
res_step:
     if (mfreeblk(buf, 0x1000) == SYSSTUSERR) {
        rets = -1;
     }
	
    va_end(ap);
    return rets;
}

// libopen.c
#include "libc.h"
hand_t open(void* file, uint_t flgs, uint_t stus)
{
    hand_t rethand = api_open(file, flgs, stus);
    return rethand;
}

// libwrite.c

#include "libc.h"
sysstus_t write(hand_t fhand, buf_t buf, size_t len, uint_t flgs)
{
    sysstus_t rets = api_write(fhand, buf, len, flgs);
    return rets;
}

上述罗列的是 文件库API, open,write函数的实现

API 指令

hand_t api_open(void* file, uint_t flgs, uint_t stus)
{
    hand_t rethand;
    //服务号: INR_FS_OPEN 0x8UL
    API_ENTRY_PARE3(INR_FS_OPEN, rethand, file, flgs, stus);
    return rethand;
}

sysstus_t api_write(hand_t fhand, buf_t buf, size_t len, uint_t flgs)
{
    sysstus_t rets;
    //服务号: INR_FS_WRITE 0xbUL
    API_ENTRY_PARE4(INR_FS_WRITE, rets, fhand, buf, len, flgs);
    return rets;
}

文件库与中断处理的API接口

内核接口

为了能够从长模式转换到实模式。通过内联汇编的方式,执行汇编代码

如果有不熟悉内联汇编的,可以看看这篇文章

应用程序运行在用户空间时,用的是用户栈,当它切换到内核空间时,用的是内核栈

所以参数的传递,就需要硬性地规定一下,要么所有的参数都用寄存器传递,要么所有的参数都保存在用户栈中

所以使用RBX、RCX、RDX、RDI、RSI 这 5 个寄存器来传递参数

#define API_ENTRY_PARE3(intnr, rets, pval1, pval2, pval3)\
__asm__ __volatile__(\
    "movq %[inr], %%rax \n\t"\
    "movq %[prv1], %%rbx \n\t"\
    "movq %[prv2], %%rcx \n\t"\
    "movq %[prv3], %%rdx \n\t"\
    "int $255 \n\t"\
    "movq  %%rax,%[retval] \n\t"\
    :[retval] "=r" (rets)\
    :[inr] "r" (intnr),[prv1]"g" (pval1),\
    [prv2] "g" (pval2),[prv3]"g" (pval3)\
    :"rax", "rbx", "rbx", "rcx", "rdx", "cc", "memory"\
)

/**
 * 传递四个参数所用的宏
 *  1. 系统服务号: movq %[inr], %%rax
 *  2. 第一个参数: movq %[prv1], %%rbx
 *  3. 第二个参数: movq %[prv2], %%rcx
 *  4. 第三个参数: movq %[prv3], %%rdx
 *  5. 第四个参数: movq %[prv4], %%rsi
 *  6. 触发中断: int $255. int 指令后面需要跟一个常数
 *               这个常数表示 CPU 从中断表描述符表中取得第几个中断描述符进入内核
 *  7. 处理返回结果: movq %%rax, %[retval]
 */
#define API_ENTRY_PARE4(intnr, rets, pval1, pval2, pval3, pval4)\
__asm__ __volatile__(\
    "movq %[inr], %%rax \n\t"\
    "movq %[prv1], %%rbx \n\t"\
    "movq %[prv2], %%rcx \n\t"\
    "movq %[prv3], %%rdx \n\t"\
    "movq %[prv4], %%rsi \n\t"\
    "int $255 \n\t"\
    "movq %%rax, %[retval] \n\t"\
    :[retval] "=r" (rets)\
    :[inr] "r" (intnr), [prv1]"g" (pval1),\
    [prv2] "g" (pval2), [prv3]"g" (pval3),\
    [prv4] "g" (pval4)\
    :"rax", "rbx", "rcx", "rdx", "rsi", "cc", "memory"\
)

以上就是应用程序向 CPU 发送一个中断信号,CPU 接受到这个电子信号后,在允许响应中断的情况下,就会中断当前正在运行的程序,自动切换到相应的 CPU R0 特权级,并跳转到中断门描述符中相应的地址上运行中断处理代码

如果对中断,不甚了解,可以看看这篇文章

中断门

注册中断描述符

/**
 * @brief 初始化中断描述符
 *  1. 中断向量号是中断描述符的索引,当处理器收到一个外部中断向量后
 *       它用此向量号在中断描述符表中查询对应的中断描述符,然后再去执行该中断中的中断处理程序
 *  2. 由于中断描述符是8个字节(64位),所以处理器用中断向量号乘以8后
 *       再与IDTR中的中断描述符表地址相加,所求的地址之和便是该中断向量号对应的中断描述符
 * 
 * @return void
 */
PUBLIC LKINIT void init_idt_descriptor()
{ 
   .....
    // 中断向量表,绑定中断编号255和exi_sys_call,特权级别设置为PRIVILEGE_USER
    // INT_VECTOR_SYSCAL  0xFF
    set_idt_desc(INT_VECTOR_SYSCALL, DA_386IGate, exi_sys_call, PRIVILEGE_USER);

    set_iidtr(x64_idt);
    load_x64_idt(&x64_iidt_reg);
}

对应的中断描述符`

exi_sys_call:
	EXI_SCALL
; 软中断
%macro  EXI_SCALL  0
	
	;push rax
	push rbx	; 保存通用寄存器到内核栈
	push rcx
	push rdx
	push rbp
	push rsi
	push rdi
        .....
        mov	rdi, rax	        ; 处理hal_syscl_allocator函数第一个参数inr
	mov rsi, rsp	                ; 处理hal_syscl_allocator函数第二个参数krnlsframp
	call hal_syscl_allocator	; call hal_syscl_allocator 
        .....
       
    pop rdi
	pop rsi
	pop rbp
	pop rdx
	pop rcx
	pop rbx	; 从内核栈中恢复通用寄存器
	;pop rax
	
	iretq	; 中断返回

中断处理分发

sysstus_t hal_syscl_allocator(uint_t inr, void* krnlsframp)
{
    cpuflg_t cpuflg;
    sysstus_t ret;
    hal_cpuflag_sti(&cpuflg);

    // 系统服务分发器
    ret = krlservice(inr, krnlsframp);

    hal_cpuflag_cli(&cpuflg);
    return ret;
}

系统服务分发器

sysstus_t krlservice(uint_t inr, void* sframe)
{
    // 判断服务号是否大于最大服务号
    if (INR_MAX <= inr) {
        return SYSSTUSERR;
    }

    // 判断是否有服务接口函数
    if (NULL == osservicetab[inr]) {
        return SYSSTUSERR;
    }
    
    return osservicetab[inr](inr, (stkparame_t*)sframe);
}

系统服务接口

// 服务接口
KRL_DEFGLOB_VARIABLE(syscall_t,osservicetab)[INR_MAX] = {
    NULL,krlsvetabl_mallocblk,              // 内存分配服务接口
    krlsvetabl_mfreeblk,                    // 内存释放服务接口
    krlsvetabl_exel_thread,                 // 进程服务接口
    krlsvetabl_exit_thread,                 // 进程退出服务接口
    krlsvetabl_retn_threadhand,             // 获取进程id服务接口
    krlsvetabl_retn_threadstats,            // 获取进程状态服务接口
    krlsvetabl_set_threadstats,             // 设置进程状态服务接口
    krlsvetabl_open,krlsvetabl_close,       // 文件打开、关闭服务接口
    krlsvetabl_read,krlsvetabl_write,       // 文件读、写服务接口
    krlsvetabl_ioctrl,krlsvetabl_lseek,     // 文件随机读写和控制服务接口
    krlsvetabl_time                         // 获取时间服务接口
};

系统服务:打开文件

// open 文件接口
sysstus_t krlsvetabl_open(uint_t inr, stkparame_t *stkparv)
{
    if (inr != INR_FS_OPEN) {
        return SYSSTUSERR;
    }

    return (sysstus_t)krlsve_open((void *)stkparv->parmv1, (uint_t)stkparv->parmv2, (uint_t)stkparv->parmv3);
}

hand_t krlsve_open(void *file, uint_t flgs, uint_t stus)
{
    if (file == NULL) {
        return SYSSTUSERR;
    }

    return krlsve_core_open(file, flgs, stus);
}

/**
 * @brief 打开文件核心函数
 *  1. 获取当前运行的进程
 *  2. 通过对应的句柄寻找对应设备
 *  3. 往进程描述符组中写入对应的资源信息
 *  4. 调用设备驱动
 * 
 * @param file 句柄
 * @param flgs 句柄flag
 * @param stus 句柄status
 * @return hand_t 进程描述符组的位置
 */
hand_t krlsve_core_open(void *file, uint_t flgs, uint_t stus)
{
    devid_t devid = {0, 0, 0};
    device_t *devp = NULL;
    objnode_t *ondp = NULL;
    hand_t rethd = NO_HAND;
   
    // 获取当前运行的进程
    thread_t *tdp = krlsched_retn_currthread();
    if (((flgs & FILE_TY_MASK) == FILE_TY_DEV)) {
        goto op_dev_step;
    }

    if (((flgs & FILE_TY_MASK) == FILE_TY_FILE)) {
        goto op_fil_step;
    }

    return NO_HAND;
op_dev_step:
    devid.dev_mtype = ((devid_t *)file)->dev_mtype;
    devid.dev_stype = ((devid_t *)file)->dev_stype;
    devid.dev_nr = ((devid_t *)file)->dev_nr;

    // 通过对应的句柄寻找对应设备
    devp = krlonidfl_retn_device((void *)(&devid), DIDFIL_IDN);
    if (devp == NULL) {
        return NO_HAND;
    }

    ondp = krlnew_objnode();
    if (ondp == NULL) {
        return NO_HAND;
    }

    ondp->on_opercode = IOIF_CODE_OPEN;
    ondp->on_objtype = OBJN_TY_DEV;
    ondp->on_objadr = devp;

    // 往进程描述符组中写入对应的资源信息
    rethd = krlthd_add_objnode(tdp, ondp);
    if (rethd == NO_HAND) {
        goto res_step;
    }
     
    // 调用设备驱动
    if (krlsve_open_device(ondp) == SYSSTUSERR) {
        goto res_step;
    }

    return rethd;
}

系统服务: 写文件

// write 文件接口
sysstus_t krlsvetabl_write(uint_t inr, stkparame_t *stkparv)
{
    if (inr != INR_FS_WRITE) {
        return SYSSTUSERR;
    }

    return krlsve_write((hand_t)stkparv->parmv1, (buf_t)stkparv->parmv2, (size_t)stkparv->parmv3, (uint_t)stkparv->parmv4);
}

sysstus_t krlsve_write(hand_t fhand, buf_t buf, size_t len, uint_t flgs)
{
    if (fhand <= NO_HAND || fhand >= TD_HAND_MAX || buf == NULL || len == 0) {
        return SYSSTUSERR;
    }

    return krlsve_core_write(fhand, buf, len, flgs);
}

/**
 * @brief 写文件核心实现
 *  1. 获取当前运行的进程
 *  2. 获取进程描述符组对应下标的设备资源
 * 
 * @param fhand 进程描述符组下表
 * @param buf 待写入的信息
 * @param len 信息长度
 * @param flgs 
 * @return sysstus_t 
 */
sysstus_t krlsve_core_write(hand_t fhand, buf_t buf, size_t len, uint_t flgs)
{

    thread_t *currtd = krlsched_retn_currthread();
    objnode_t *onp = krlthd_retn_objnode(currtd, fhand);
    if (onp == NULL) {
        return SYSSTUSERR;
    }

    if (onp->on_objtype == OBJN_TY_DEV || onp->on_objtype == OBJN_TY_FIL) {
        onp->on_opercode = IOIF_CODE_WRITE;
        onp->on_buf = buf;
        onp->on_len = len;
        onp->on_bufsz = 0x1000;
        onp->on_acsflgs = flgs;

        return krlsve_write_device(onp);
    }

    return SYSSTUSERR;
}

// 调用文件系统
sysstus_t krlsve_write_device(objnode_t *ondep)
{
    if (ondep->on_objadr == NULL) {
        return SYSSTUSERR;
    }

    if (krldev_io(ondep) == DFCERRSTUS) {
        return SYSSTUSERR;
    }

    return SYSSTUSOK;
}


驱动回调函数

/**
 * @brief 发送设备IO
 * 
 * @param nodep 
 * @return drvstus_t 
 */
drvstus_t krldev_io(objnode_t *nodep)
{
    // 获取设备对象
    device_t *devp = (device_t *)(nodep->on_objadr);
    // 检查操作对象类型是不是文件或者设备,对象地址是不是为空
    if ((nodep->on_objtype != OBJN_TY_DEV && nodep->on_objtype != OBJN_TY_FIL) || nodep->on_objadr == NULL) {
        return DFCERRSTUS;
    }

    // 检查IO操作码是不是合乎要求
    if (nodep->on_opercode < 0 || nodep->on_opercode >= IOIF_CODE_MAX) {
        return DFCERRSTUS;
    }

    return krldev_call_driver(devp, nodep->on_opercode, 0, 0, NULL, nodep);
}

/**
 * @brief 调用设备驱动
 *  1. IO 操作码为索引调用驱动程序功能分派函数数组中的函数,并把设备和 objnode_t 结构传递进去
 * 
 * @param devp 设备指针
 * @param iocode 操作码
 * @param val1 
 * @param val2 
 * @param p1 
 * @param p2 
 * @return drvstus_t 
 */
drvstus_t krldev_call_driver(device_t *devp, uint_t iocode, uint_t val1, uint_t val2, void *p1, void *p2)
{
    driver_t *drvp = NULL;
    // 检查设备和IO操作码
    if (devp == NULL || iocode >= IOIF_CODE_MAX) {
        return DFCERRSTUS;
    }

    drvp = devp->dev_drv;
    // 检查设备是否有驱动程序
    if (drvp == NULL) {
        return DFCERRSTUS;
    }

    // 用IO操作码为索引调用驱动程序功能分派函数数组中的函数
    return drvp->drv_dipfun[iocode](devp, p2);
}

文件驱动: 文件系统

**
 * @brief 设置驱动默认函数
 * 
 * @param drvp 驱动指针
 */
void rfs_set_driver(driver_t *drvp)
{
    drvp->drv_dipfun[IOIF_CODE_OPEN] = rfs_open;
    drvp->drv_dipfun[IOIF_CODE_CLOSE] = rfs_close;
    drvp->drv_dipfun[IOIF_CODE_READ] = rfs_read;
    drvp->drv_dipfun[IOIF_CODE_WRITE] = rfs_write;
    drvp->drv_dipfun[IOIF_CODE_LSEEK] = rfs_lseek;
    drvp->drv_dipfun[IOIF_CODE_IOCTRL] = rfs_ioctrl;
    drvp->drv_dipfun[IOIF_CODE_DEV_START] = rfs_dev_start;
    drvp->drv_dipfun[IOIF_CODE_DEV_STOP] = rfs_dev_stop;
    drvp->drv_dipfun[IOIF_CODE_SET_POWERSTUS] = rfs_set_powerstus;
    drvp->drv_dipfun[IOIF_CODE_ENUM_DEV] = rfs_enum_dev;
    drvp->drv_dipfun[IOIF_CODE_FLUSH] = rfs_flush;
    drvp->drv_dipfun[IOIF_CODE_SHUTDOWN] = rfs_shutdown;
    drvp->drv_name = "rfsdrv";
    return;
}


// 串联整合
drvstus_t rfs_open(device_t *devp, void *iopack)
{
    objnode_t *obp = (objnode_t *)iopack;

    // 根据objnode_t结构中的访问标志进行判断
    if (obp->on_acsflgs == FSDEV_OPENFLG_OPEFILE) {
        return rfs_open_file(devp, iopack);
    }

    if (obp->on_acsflgs == FSDEV_OPENFLG_NEWFILE) {
        return rfs_new_file(devp, obp->on_fname, 0);
    }

    return DFCERRSTUS;
}

/**
 * @brief 打开文件的接口函数
 *  1. 从 objnode_t 结构的文件路径提取文件名
 *  2. 获取根目录文件,在该文件中搜索对应的 rfsdir_t 结构,看看文件是否存在
 *  3. 分配一个 4KB 缓存区,把该文件对应的 rfsdir_t 结构中指向的逻辑储存块读取到缓存区中,然后释放根目录文件
 *  4. 把缓冲区中的 fimgrhd_t 结构的地址,保存到 objnode_t 结构的 on_finode 域中
 * 
 * @param devp 
 * @param iopack 
 * @return drvstus_t 
 */
drvstus_t rfs_open_file(device_t *devp, void *iopack)
{
    objnode_t *obp = (objnode_t *)iopack;
    // 检查objnode_t中的文件路径名
    if (obp->on_fname == NULL) {
        return DFCERRSTUS;
    }

    // 调用打开文件的核心函数
    void *fmdp = rfs_openfileblk(devp, (char_t *)obp->on_fname);
    if (fmdp == NULL) {
        return DFCERRSTUS;
    }

    // 把返回的fimgrhd_t结构的地址保存到objnode_t中的on_finode字段中
    obp->on_finode = fmdp;
    return DFCOKSTUS;
}



// 文件操作的接口
drvstus_t rfs_write(device_t *devp, void *iopack)
{
    // 调用写文件操作的接口函数
    return rfs_write_file(devp, iopack);
}

/**
 * @brief 写入文件数据的接口函数
 *  1. 检查 objnode_t 结构中用于存放文件数据的缓冲区及其大小
 *  2. 检查 imgrhd_t 结构中文件相关的信息
 *  3. 把文件的数据读取到 objnode_t 结构中指向的缓冲区中
 * 
 * @param devp 
 * @param iopack 
 * @return drvstus_t 
 */
drvstus_t rfs_write_file(device_t *devp, void *iopack)
{
    objnode_t *obp = (objnode_t *)iopack;
    // 检查文件是否已经打开,以及用于存放文件数据的缓冲区和它的大小是否合理
    if (obp->on_finode == NULL || obp->on_buf == NULL || obp->on_bufsz != FSYS_ALCBLKSZ) {
        return DFCERRSTUS;
    }

    return rfs_writefileblk(devp, (fimgrhd_t *)obp->on_finode, obp->on_buf, obp->on_len);
}

总结

通过文件open 在进程中创建一个设备描述符,并返回它在进程中的ID

然后通过这个描述符ID,对文件进行write操作

标签:rfs,return,操作系统,应用软件,devp,uint,objnode,执行,buf
From: https://blog.csdn.net/weixin_40398522/article/details/137422182

相关文章

  • 操作系统综合题之“银行家算法,计算还需要资源数量和可用资源梳理和写出安全队列和银行
    一、设系统中有三种类型资源A、B、C,资源数量分别为15、7、18,系统有五个进程P1、P2、P3、P4、P5,其最大资源需求量分别为(5,4,9)、(4,3,5)、(3,0,5)、(5,2,5)、(4,2,4)。在T0时刻,系统为个进程已经分配的资源数量分别为(2,1,2)、(3,0,2)、(3,0,4)、(2,0,4)、(3,1,4)。若系统采用银行家算法实施死锁避免策略......
  • 操作系统综合题之“银行家算法,画出试分配后的资源分配状态图”
    一、问题:假设一个系统,有5个进程P0、P1、P2、P3、P4,有3种类型的资源A、B和C。A类资源有10个,B类资源有5个,C类资源有7个。假定在T0时刻,系统的资源分配状态如图所示 在T0时刻,可以找到一个安全序列【P1,P3,P4,P2,P0】,系统在T0时刻处于安全状态1.若此进程P1提出资源请求request1=......
  • 操作系统综合题之“银行家算法,计算各资源总数和Need还需要数量”
    一、问题:某系统在某时刻的进程和资源状态如下表所示:进程Allocation(已分配资源数)(ABCD)Max(最大需要资源数)(ABCD)Avaliable(可用资源数)(ABCD)P1001102221520P2112......
  • 操作系统综合题之“给进程数和资源数,判断是否安全状态和列出安全序列”
    一、问题:若有3个进程共享9个资源,且当前资源分配情况如下进程已占资源数最大需求数P126P236P315 请回答以及下问题1.目前系统是否处于安全状态?2.如果是,给出进程执行的安全序列,如果不是,请说明理由 二、参考答案1.目前处于安全状态2.安全序列为:P......
  • 【MySQL系列】--SQL 执行顺序
    不想往后翻直接告诉我结论,好的:)FROM:获取第一张表,称为原表table1,获取第二张表,称为原表table2,将两张表做笛卡尔积,生成第一张中间表Temp1。ON:根据筛选条件,在Temp1上筛选符合条件的记录,生成中间表Temp2。JOIN:根据连接方式的不同,选择是否在Temp2的基础上添加外部行。左外......
  • ctfshow--红包题第二弹 临时文件命令执行
    上来先代码审计点击查看代码<?phpif(isset($_GET['cmd'])){$cmd=$_GET['cmd'];highlight_file(__FILE__);if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){die("cerror");......
  • 操作系统综合题之“采用实时调度,可调度的限制条件和可调度的最大X值是是多少ms的CPU时
    一、问题:单处理器情况下,m个周期性实时进程,若进程i处理时间为Ci,周期时间为Pi<(1≤i ≤m)1.要使系统可调度的限制条件什么?2.设置一个实时系统使用了4个周期事件,其周期分别为50ms,100ms,200ms,200ms。假设这4个周期时间分别需要25ms,20ms,10ms和xms的CPU时间。保持系统可调度的最......
  • 操作系统综合题之“采用实时调度,6个实时进程是否能保证都在截止时间完成吗?”
    一、问题:为了实现实时调度,系统需要为调度程序提供那些信息?(至少写出4个)在单处理机情况下,如果有6个实时进程,周期时间都是30ms,系统为每个进程分配6ms的处理时间,请问系统能否保证每个实时进程都能在截止时间内完成吗?为什么? 二、参考答案答:1.系统需要提供的信息:就绪时间,开始截止时......
  • Spring/SpringBoot/SpringCloud Mybatis 执行流程
    在后续分析Mybatis流程中代码的可能会用到IDEAdebug技巧:条件断点代码断点,右键勾选弹窗Condition:写入表达式回到上一步:Java动态代理实现InvocationHandler接口:packagecom.lvyuanj.core.test;importcom.lvyuanj.core.model.User;importorg.apache.......
  • Linux操作系统之nfs网络文件系统
    目录一、NFS简介1.2安装配置NFS 一、NFS简介nfs类似于windows文件共享将linux的一个目录共享到网络中,网络中的其他所有主机都可以使用这个共享目录中的文件samba文件共享可以在linux中通过samba共享一个目录,然后在linux中可以访问这个共享 nfs网络磁盘可......