操作系统相关杂项

news2024/11/23 10:23:35

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、
      • dlopen, dlerror, dlclose
      • 直接执行动态库中的某个函数/某段代码
  • Linux共享库的组织
      • 共享库的构造和析构函数
  • 动态链接堆栈初始化
  • C++全局构造与析构
  • 模拟实现库函数 fread
  • syscall
    • syscall 原理
    • 基于int的Linux的经典系统调用实现


前言


一、

dlopen, dlerror, dlclose

#include <dlfcn.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    void* handle;
    using fty = double(*)(double);
    fty func;
    char* error;

    handle = dlopen(argv[1], RTLD_NOW);
    if (handle == nullptr) {
        printf("%s\n", dlerror());
        return -1;
    }
    func = (fty) dlsym(handle, "sin");
    if ( (error = dlerror()) != nullptr ) {
        printf("Symbol sin not found: %s\n", error);
        goto exit_runso;
    }
    printf("%f\n", func(3.1415926 / 2));

    exit_runso:
    dlclose(handle);
}

直接执行动态库中的某个函数/某段代码

./a.out /libxxx.so funname arg1 arg2 ... return_type

编译不通过且看不懂

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

#define SETUP_STACK         \
    i = 2;                   \
    while (++i < argc - 1)   \
    {                           \
        switch (argv[i][0])     \
        {                       \
        case 'i':\
            asm volatile("push %0" ::\
                "r"(atoi(&argv[i][1])) );  \
            esp += 4;                      \
            break;                         \
        case 'd':                          \
            atof(&argv[i][1]);             \
            asm volatile("subl $8, %esp\n" \
            "fstpl (%esp)" );\
            esp += 8;\
            break;\
        case 's':\
            asm volatile("push %0" ::\
            "r"(&argv[i][1])) ;\
            esp += 4;\
            break;\
        default:\
            printf("error argument type");\
            goto exit_runso;\
        }\
    }

#define RESTORE_STACK\
    asm volatile("add %0, %%esp" :: "r"(esp))

int main(int argc, char *argv[])
{
    void* handle;
    char* error;
    int i;
    int esp = 0;
    void* func;

    handle = dlopen(argv[1], RTLD_NOW);
    if (handle == 0) {
        printf("Can't find library: %s\n", argv[1]);
    }
    
    func = dlsym(handle, argv[2]);
    if ( (error = dlerror()) != NULL ) {
        printf("Find symbol %s error: %s\n", argv[2], error);
    }

    switch (argv[argc-1][0])
    {
    case 'i':
    {
        typedef int(*f)();
        f func_int = (f)func;
        SETUP_STACK;
        int ret = func_int();
        RESTORE_STACK;
        printf("ret = %d\n", ret);
        break;
    }
    case 'd':
    {
        typedef double(*f)();
        f func_double = (f)func;
        SETUP_STACK;
        double ret = func_double();
        RESTORE_STACK;
        printf("ret = %f\n", ret);
        break;
    }
    case 's':
    {
        typedef char*(*f)();
        f func_str = (f)func;
        SETUP_STACK;
        char* ret = func_str();
        RESTORE_STACK;
        printf("ret = %s\n", ret);
        break;
    }
    case 'v':
    {
        typedef void(*f)();
        f func_void = (f)func;
        SETUP_STACK;
        func_void();
        RESTORE_STACK;
        printf("ret = void\n");
        break;
    }
    }  // end of switch

    exit_runso:

    dlclose(handle);
}

Linux共享库的组织

共享库命名规则:
libname.so.x.y.z
x: 主版本号,y: 次版本号,z: 发布版本号
主版本号: 重大的不兼容升级,个版本之间不兼容
次版本号: 库的增量升级,增加了一些新的接口符号,且保持原来的符号不变
**发布版本号:**表示库的一些错误修正、性能改进等

在这里插入图片描述
LD_LIBRARY_PATH=/home/user /bin/ls
另一种方式
/lib/ld-linux.so.2 -library-path /home/user /bin/ls
动态连接器找查共享库的顺序:

  • 由环境变量LD_LIBRARY_PATH指定的路径
  • 由路径缓存文件/etc/ld.so.cache指定的路径
  • 默认共享库目录,先/usr/lib,然后/lib
# 生共享库,并指定soname
gcc -c -g -Wall -o libfoo1.o libfoo1.c
gcc -c -g -Wall -o libfoo2.o libfoo2.c
gcc -shared -fPIC -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0.0 \
libfoo1.o libfoo2.o -lbar -lbar2
ld -rpath /home/mylib -o program.out program.o -lsomelib

这样产生的输出可执行文件program.out在被动态连接器装载时,动态连接器会首先在"/home/mylib"找查共享库

strip libfoo.so  # 清除掉共享库或可执行文件的所有符号和调试信息
ldconfig -n shared_library_directory

共享库的构造和析构函数

在函数声明时加上“attribute((constructor))”的属性,即指定该函数为共享库构造函数,拥有这种属性的函数会在共享库加载时被执行,即在main函数之前执行。如果使用dlopen()打开共享库,共享库构造函数会在dlopen()返回之前被执行。

“__attribute((destructor))”析构函数,在main函数执行完毕之后执行(或程序调用exit()时执行)如果共享库时运行时加载的,析构函数会在dlclose()返回之前执行。

void __attribute__((constructor(数字越小优先级越高))) init_function(void);
void __attribute__((destructor(与构造相反)))  fini_function(void);

__attribute__语法是GCC对c/c++语言的扩展,在其他编译器上这种语法并不通用

动态链接堆栈初始化

#include <stdio.h>
#include <stdint.h>
#include <elf.h>

int main(int argc, char* argv[])
{
    printf("addr argc: %x\n", &argc);

    uintptr_t* p = (uintptr_t*)argv;

    printf("p-1: %x\n", p-1);


    printf("argument number: %d\n", *(int*)(p-1) );
    printf("\narguments:\n");
    char** tmp = argv;
    while (*tmp)
    {
        printf("%s\n", *tmp);
        ++tmp;
    }
    p += argc;
    ++p;
    printf("\nenv info:\n");
    char** tmp2 = (char**)p;
    while (*tmp2)
    {
        
        printf("%s\n", *tmp2);
        // ++p;
        ++tmp2;
        // tmp2 = (int*)p;
    }
    p = (uintptr_t*)tmp2;
    ++p;
    printf("\nAuxiliary Vectors::\n");
    Elf64_auxv_t* aux = (Elf64_auxv_t*)p;
    while (aux->a_type != AT_NULL)
    {
        printf("Type: %02d Value: %x\n", aux->a_type, aux->a_un.a_val);
        ++aux;
    }
    
}

函数调用
在这里插入图片描述

一个C语言运行库大致包含:

  • 启动与退出:包括入口函数及入口函数所依赖的其他函数
  • 标准函数:由C语言标准规定的函数
  • I/O:I/O功能的封装和实现
  • 堆:堆的封装和实现
  • 语言实现:语言中一些特殊功能的实现
  • 调试:实现调试功能的代码

C++全局构造与析构

“.init”和“.finit”段的代码最终会被拼成_init()和_finit()函数


void my_init(void)
{
	printf("hello\n");
}
typedef void(*ctor_t)();
// 在.ctors段里添加一个函数指针
ctor_t __attribute__((section(".ctors"))) my_init_p = &my_init;

或者:
void my_init(void) __attribute__((constructor));
void my_init(void)
{
	printf("hello\n");
}
#pragma section(".CRT$XCA", long, read)
#pragma section(".CRT$XCZ", long, read)

#define _CRTALLOC(x) __declspec(allocate(x))
其后的变量将被分配在段x里

#pragma section("section-name" [, attributes])
生成名为"section-name"的段并具有attributes属性

模拟实现库函数 fread

int fflush(FILE* stream);
int setvbuf(FILE* stream, char* buf, int mode, size_t size);
	mode: _IONBF 无缓冲
		  _IOLBF 行缓冲,仅用于文本文件,遇到换行就输出
		  _IOFBF 仅当缓冲满时才进行flush
void setbuf(FILE* stream, char* buf);
== 设置文件缓冲 setvbuf(stream buf, _IOFBF, BUFSIZ);

syscall

Linux使用0x80号中断作为系统调用的入口
Windows使用0x2E号中断作为系统调用入口

x86下,Linux系统调用由0x80中断完成,各个通用寄存器用于传递参数,EAX寄存器用于表示系统调用的接口号,比如EAX = 1表示退出进程(exit);EAX=2表示创建进程(fork);EAX=3表示读取文件或IO(read);EAX=4表示写文件或IO(write),每个系统调用都对应与内核源代码中的一个函数,它们都以“sys_”开头,比如exit调用对应内核中的sys_exit函数。当系统调用返回时,EAX又作为调用结果的返回值。

在这里插入图片描述
在这里插入图片描述

这些系统调用的C语言形式在<unistd.h>中

syscall 原理

  1. cpu每过一段时间去看一看有没有系统调用
  2. 发生系统调用时向cpu发送个信号,CPU收到后再去处理

将系统调用号放入eax寄存器,然后使用int 0x80调用中断,中断服务程序从eax里取的系统调用号,进而调用对应的函数

基于int的Linux的经典系统调用实现

在这里插入图片描述

#define _syscall0(type, name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name)); \
__syscall_return(type, __res); \
}

syscall0(pid_t, fork)展开后
pid_t fork(void)
{
	long __res;
	__asm__ volatile ("int $0x80"
		: "=a" (__res)
		: "0" (__NR_fork));
	__syscall_return(pid_t, __res);
}
易读形式
pid_t fork(void)
{
	long __res;
	$eax = __NR_fork
	int $0x80
	__res = $eax
	__syscall_return(pid_t, __res);
}
__NR_fork是一个宏,表示fork系统调用的调用号
#define __syscall_return(type, res) \
do { \
	if ((unsigned long) res >= (unsigned long)(-125)) { \
		errno = -(res); \
		res = -1; \
	} \
	return (type)(res); \
} while (0)
这个宏用于检查系统调用的返回值,并把它相应的转换为C语言的errno错误码

汇编后得到类似代码:
fork:
mov eax, 2
int 0x80
cmp eax, 0xFFFFFF83
jb syscall_noerror
neg eax
mov errno, eax
mov eax, 0xFFFFFFFF
syscall_noerror:
ret

  当用户调用某个系统调用的时候,实际是执行了以上一段汇编代码。CPU执行到 int $0x80 时,会保存现场以便恢复,接着会将特权状态切换到内核态。然后CPU便会找查中断向量表中的第0x80号元素。
  在实际执行中断向量表中的第0x80号元素所对应的函数之前,CPU首先还要进行栈的切换。在Linux中,用户态和内核态使用的是不同的栈,两者各自负责各自的函数调用,互不干扰。但在应用程序调用0x80号中断时,程序的执行流程从用户态切换到内核态,这时程序的当前栈必须也相应地从用户态切换到内核态。从中断处理函数中返回时,程序的当前栈还要从内核栈切换回用户栈。
  “当前栈”,指的是ESP的值所在的栈空间。如果ESP的值位于用户栈的范围内,那么程序的当前栈就是用户栈,反之亦然。此外,寄存器SS的值还应该指向当前栈所在的页。将当前栈由用户栈切换为内核栈的实际行为就是:

  • (1)保存当前ESP, SS的值
  • (2)将ESP, SS的值设置为内核栈的相应值
    反回来,内核态切换为用户态:
  • (1)恢复原来的ESP, SS的值
  • 用户态的ESP和SS的值保存在内核栈上(每个进程都有自己的内核栈)
  • 在内核栈中依次压入用户态的寄存器SS, ESP, EFLAGS, CS, EIP

在这里插入图片描述

中断时用户栈和内核栈切换

在这里插入图片描述

Linux i386中断服务程序

在这里插入图片描述

Linux 系统调用流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1050337.html

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

相关文章

idea Springboot 图书管理系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 图书管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#…

基于Java实现的民宿预订管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

yolox相关

yolox YOLOXYOLOX-DarkNet53yolov3作为baseline输入端Strong data augmentationMosaic数据增强MixUp数据增强注意 BackboneNeckPrediction层Decoupled headDecoupled Head 细节 Anchor-freeAnchor Based方式Anchor Free方式标签分配初步筛选精细化筛选 SimOTASimOTA Other Back…

Thymeleaf快速入门(Spring版)

文章目录 Thymeleaf快速入门&#xff08;Spring版&#xff09;1、Thymeleaf概述2、Thymeleaf快速入门3、Thymeleaf基础语法3.1 th属性3.2 标准表达式语法3.2.1 变量表达式3.2.2 选择表达式3.2.3 URL表达式3.2.3 链接表达式3.2.4 国际化表达式3.2.5 片段引用表达式 Thymeleaf快速…

机器学习——一元线性回归构造直线,并给出损失函数

目 录 Question 问题分析 1.概念补充 2.流程分析 3.注意 具体实现 最终成果 代码 思考&#xff1a; Question 在二维平面有n个点&#xff0c;如何画一条直线&#xff0c;使得所有点到该直线距离之和最短 如果能找到&#xff0c;请给出其损失函数 问题分析 1.概念…

BASH shell脚本篇2——条件命令

这篇文章介绍下BASH shell中的条件相关的命令&#xff0c;包括&#xff1a;if, case, while, until, for, break, continue。之前有介绍过shell的其它基本命令&#xff0c;请参考&#xff1a;BASH shell脚本篇1——基本命令 1. If语句 if语句用于在顺序执行语句的流程中执行条…

visio将形状、图形、文字、符合进行任意角度旋转(已解决)

第一步&#xff1a;选择一个形状&#xff0c;并选定它&#xff0c;如下图 第二步&#xff1a;在视图中&#xff0c;按顺序点击 会弹出一个位置框&#xff0c;如下图。 这里设置 角度的值 为 35deg&#xff0c;按“回车键”&#xff0c;如下图

爆肝整理,常问接口自动化测试面试题+答案(详全)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试题&#xff1…

LLMs: 强化学习从人类反馈中学习Reinforcement learning from human feedback (RLHF)

让我们考虑一下文本摘要的任务&#xff0c; 即使用模型生成一段简短的文本&#xff0c;捕捉 较长的文章中最重要的观点。 您的目标是通过向模型 展示人工生成的摘要示例&#xff0c;使用微调来提高模型的总结能力。 2020年&#xff0c;OpenAI的研究人员发表了一篇论文&#xff…

【SQL】mysql创建定时任务执行存储过程--20230928

1.先设定时区 https://blog.csdn.net/m0_46629123/article/details/133382375 输入命令show variables like “%time_zone%”;&#xff08;注意分号结尾&#xff09;设置时区&#xff0c;输入 set global time_zone “8:00”; 回车,然后退出重启&#xff08;一定记得重启&am…

定时任务管理平台青龙 QingLong

一、关于 QingLong 1.1 QingLong 介绍 青龙面板是支持 Python3、JavaScript、Shell、Typescript 多语言的定时任务管理平台&#xff0c;支持在线管理脚本和日志等。其功能丰富&#xff0c;能够满足大部分需求场景&#xff0c;值得一试。 主要功能 支持多种脚本语言&#xf…

《动手学深度学习 Pytorch版》 7.6 残差网络(ResNet)

import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l7.6.1 函数类 如果把模型看作一个函数&#xff0c;我们设计的更强大的模型则可以看作范围更大的函数。为了使函数能逐渐靠拢到最优解&#xff0c;应尽量使函数嵌套&…

Java 基于 SpringBoot 的在线学习平台

1 简介 基于SpringBoot的Java学习平台&#xff0c;通过这个系统能够满足学习信息的管理及学生和教师的学习管理功能。系统的主要功能包括首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;教师管理&#xff0c;课程信息管理&#xff0c;类型管理&#xff0c;作业信息…

F12报错前端对应请求接口未在NetWork显示

问题背景 今天看到一个接口在部分情况下为正常渲染数据 发现是后端发送数据有问题&#xff0c;但是在NetWork里面怎么都找不到 问题原因 翻看代码&#xff0c;发现是一种异步请求 内部报错了&#xff0c;所以浏览器看不到接口 具体情况 翻看控制台&#xff1a; 发现属性未…

QT用户登录注册,数据库实现

登录窗口头文件 #ifndef LOGINUI_H #define LOGINUI_H#include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QLabel> #include <QMessageBox>#include <QSqlDatabase> //数据库管理类 #include <QSqlQuery> …

【力扣每日一题】2023.9.28 花期内花的数目

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一个二维数组来表示花期&#xff0c;在一段花期之内花是开的。另外给我们一个一维数组表示来人的时间&#xff0c;要我们返回一个一…

使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理

文章目录 1、前言2、分析3、实现4、踩坑4.1、拖拽辅助线的坑4.2、数据的坑4.3、限制拖拽4.4、样式调整 1、前言 最近在做一个文件夹管理的功能&#xff0c;要实现一个树状的文件夹面板。里面包含两种元素&#xff0c;文件夹以及文件。交互要求如下&#xff1a; 创建、删除&am…

三子棋小游戏(简单详细)

设计总体思路 实现游戏可以一直玩&#xff0c;先打印棋盘&#xff0c;玩家和电脑下棋&#xff0c;最后分出胜负。 如果编写较大的程序&#xff0c;我们可以分不同模块 例如这个三子棋&#xff0c;我们可以创建三个文件 分别为&#xff1a; game.h 函数的声明game.c 函数…

求臻医学:乳腺癌治疗与基因检测 探索个性化医疗的未来

乳腺癌是全球女性最常见的恶性肿瘤&#xff0c;2020年全球新发乳腺癌病例约为230万&#xff0c;发病率超过肺癌&#xff0c;位居全部恶性肿瘤首位&#xff01;本文将为您总结乳腺癌的治疗策略与基因检测&#xff0c;揭示个性化医疗的重要意义。 乳腺癌的诊疗 早期乳腺癌通常不…

小程序echarts折线图去除圆圈

如图&#xff0c;默认的折线图上面是有圆圈的&#xff0c;鼠标放上去或者手指触摸的话会有对应的文字出现&#xff0c;但很多时候我们不需要这个圆圈&#xff0c;怎么办呢&#xff0c;其实很简单&#xff0c;只要在 series 中设置属性 showSymbol 为false 就好啦 symbol: none,…