程序执行整体概览
案例
代码例子
#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