1 UC

news2024/11/28 2:39:59

1 UC

  • 1、环境变量
  • 2、环境变量表
  • 3、错误处理
  • 4、库文件
    • 4.1 静态库
    • 4.2 动态库
    • 4.3 动态库的动态加载
  • 5、虚拟地址

1、环境变量

  • 什么是环境变量?
    • 每个进程都有一张自己的环境变量表,表中的每个条目都是形如“键=值”形式的环境变量。
    • 进程可以通过环境变量访问计算机的资凉
    • 在终端下输入env命令,可以查看环境变量列表
    • 通过echo Sname可以查看某个环境变量的值
      注意:env命令出来的都是shell中全局的环境变量
  • 环境变量的添加
    • 在终端窗口输入FOOD=zpyl,表示向当前进程中增加名为“FOOD”值为“zpyl"的环境变量,如果环境变量已经存在,则更改其值。
    • 注意:在“=”左右不要加空格
  • 环境变量的类别
    • 全局环境变量:当前shell和其子进程都是可见的
    • 局部环境变量:只有当前shell可见
    • 将局部环境变量设置成全局变量: export name
    • 删除环境变量: unset name

2、环境变量表

  • 每个进程都有一张独立的环境变量表,其中的每个条目都是一个形如“键=值”形式的环境变量
  • 所谓环境变量表就是一个以NU儿L指针结束的字符指针数组,其中的每个元素都是一个字符指针,指向一个以空字符结尾的字符串,该字符串就是形如”键=值”形式的环境变量。该指针数组的地址保存在全局变量environ中
#include <stdio.h>
int main(void){
        // 当前进程的字符指针数组的首元素地址
        extern char ** environ;
        for(char** pp=environ;*pp;pp++){
                printf("%s\n",*pp); // 输出的就是当前shell的环境变量信息
        }
        return 0;
}
  • main函数的第三个参数
#include <stdio.h>
int main(int argc,char* argv[],char* envp[]){
        // argc:命令行参数的个数
        // argv:命令行参数的内容
        // envp:环境变量表的其实地址
        // 当前进程的字符指针数组的首元素地址
        extern char ** environ;
        printf("%p,%p\n",environ,envp);// 相等
        for(char** pp=environ;*pp;pp++){
                printf("%s\n",*pp);
        }
        return 0;
}

3、错误处理

针对因为运行环境、人为操作等原因导致的错误,程序的设计者需要提前有所考虑,向函数的调用者提供必要的信息,以明确发生了结误,以及具体是什么错误。

  • 通过错误号了解具体的错误原因
    • 系统于定义的整数类型全局变量errno中存储了最近一次系统调用的错误编号
    • 头文件errno.h中包含了对errno全局变量的外部声明和各种错误号的宏定义
// 文件位置
/usr/include/errno.h
/usr/include/asm-generic/errno.h
/usr/include/asm-generic/errno-base.h
  • 相关函数
    • char* strerror(int errnum)
      • 头文件:string.h
      • 功能:将整数形式的错误号转换为有意义的字符串,返回值是参数对应的描述
    • void perror(char const* tag)
      • 头文件:stdio.h
      • 功能:在标准输出错误设备上打印最近一次函数调用的错误信息
      • tag参数是用户自定义的提示内容,在输出信息时,会将提示内容输出出来
// 错误处理
#include<stdio.h>
#include<stdlib.h>//malloc() free()
#include<errno.h> // int errno
#include <string.h> // strerror

int main(){
        int *p = malloc(0xfffffffffffffff);
        if(p==NULL){
                printf("malloc失败\n");
                printf("errno = %d\n",errno); //cat /usr/include/asm-generic/errno-base.h 
                printf("malloc:%s\n",strerror(errno));
                perror("在程序第7行:");
                return -1;
        }
        free(p);
        p=NULL;
        return 0;
}
  • 执行结果
    在这里插入图片描述

4、库文件

  • 单一模型:
    • 将程序中所有功能全部实现于一个单一的源文件内部。编译时间长,不易于维护和升级,不易于协作开发。
  • 分离模型:
    • 将程序中的不同功能模块划分到不同的源文件中。缩短编译时间,易于维护和升级,易于协作开发。
  • 库文件:将多个目标文件统一整理合成一个库文件,一般分为两种,一种是静态库,一种是动态库。

4.1 静态库

  • 静态库的本质就是将多个目标文件打包成一个文件。
  • 链接静态库就是将库中被调用的代码复制到调用模块中
  • 静态库的拓展名是.a 例:libxxx.a
    构建静态库
  • 实现相关函数
  • 编译成目标文件
  • 打包成静态库
    (1)编辑相关的代码和接口声明
// 书写calc.h calc.c show.h show.c
// calc.h文件
#ifndef __CALC_H_
#define __CALC_H_
// 加法
int add(int,int);
// 减法
int sub(int,int);
#endif

// calc.c文件
#include "calc.h"
int add(int a,int b){
    return a + b;
}
int sub(int a,int b){
    return a - b;
}

// show.h
#ifndef __SHOW_H_
#define __SHOW_H_
void show(int,char,int,int);// 显示等式
#endif

// show.c
#include "show.h"
#include <stdio.h>
void show(int a,char op,int b,int c){
        printf("%d %c %d = %d\n",a,op,b,c);
}

(2) 编译成目标文件

gcc -c calc.c
gcc -c show.c

(3) 打包成静态库

ar -r libmath.a calc.o show.o

注意:静态库的名称是以lib开头,以.a为扩展名的

使用静态库

// 使用
#include <stdio.h>
#include "calc.h"
#include "show.h"
int main(){
        int a=8,b=5;
        show(a,'+',b,add(a,b));
        show(a,'-',b,sub(a,b));
        return 0;
}
// 与静态库在同一个目录下时
gcc main.c libmath.a
// 与静态库不在一个目录下时
gcc main.c -lmath -L..
// -l 库名,不需要书写开头的lib  -L库路径
// 也可以使用环境变量来指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath

4.2 动态库

  • 动态库和静态库不同,链接动态库不需要将被调用的函数代码复制到包含调用代码的可执行文件中,相反链接器会在调用语句处嵌入一段指令,在该程序执行到这段指令时,会加载该动态库并寻找被调用函数的入口地址并执行之。
  • 如果动态库中的代码同时为多个进程所用,动态库在内存的实例仅需一份,为所有使用该库的进程所共享,因此动态库亦称共享库。
  • 动态库的拓展名是.so 例libxxx.so
    构建静态库
  • 实现相关函数
  • 编译成目标文件
  • 打包成动态库
    (1)实现相关的函数,这里使用上文提到的calc.h calc.c show.h show.c
    (2)编译成目标文件
gcc -c -fpic calc.c 
gcc -c -fpic show.c 
// -fpic 在编译阶段告诉编译器产生位置无关码

(3)打包成动态库

gcc -shared calc.o show.o -o libmath.so
// 其中文件名必须以lib开头,扩展名为so

(4)使用

gcc main.c libmath.so 

注意:在生成的可执行文件执行时,会抛出一下异常,这是因为动态库在链接过程中,链接器会找到库所在的位置,所以需要做一下的配置,在环境变量中加入库所在的文件
在这里插入图片描述

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

再次执行即可

  • 总结
    PATH:bash用来找命令的
    LIBRARY_PATH:gcc在编译阶段找库用的
    LD_LIBRARY_PATH:链接器在执行阶段找库用的

  • 静态库和动态库的优缺点:

    • 静态库的体积比动态库要大,执行的效率比动态库要高,在执行过程静态库不依赖库本身
    • 动态库在执行过程中依赖库本身,链接器需要找到依赖库,所以动态库更有利于软件更新

4.3 动态库的动态加载

在程序执行的过程中,当需要链接动态库中的函数时则加载,反之则释放,以下是常用函数
1:dlopen

void* dlopen(char const* filename,int flag);
- 功能:将共享库载入内存并获得其访问句柄
- 参数:
	- filename:动态库路径,若只给文件名不带目录,则根据LD_LIBRARY_PATH环境变量的值搜索动态库
	- flag:加载方式,可取以下值:
			RTLD_LAZY-延迟加载,使用动态库中的符号时才真的加载进内存
			RTLD_NOW-立即加载。
- 返回值:成功返回动态库的访问句柄,失败返回NULL- 句柄:句柄唯一地标识了系统内核所维护的共享库对象,将作为后续函数调用的参数

2:dlsym

void* dlsym(void* handle,char const* symbol);
- 功能:从已被加载的动态库中获取特定名称的符号地址
- 参数:
	- handle:动态库访问句柄
	- symbol:符号名
- 返回值:成功返回给定符号的地址,失败返回NULL- 该函数所返回的指针为void* 类型,需要造型为与实际目标类型相一致的指针后才能使用。

3:dlclose

int dlclose(void* handle);
- 功能:从内存中卸载动态库
- 参数:
	- handle:动态库句柄
- 返回值:成功返回0,失败返回非0- 注意:所卸载的共享库未必会真的从内存中立消失,因为其他程序可能还需要使用该库,只有所有使用该库的程序都显示或隐式地卸载了该库,该库所占用的内存空间才会真正得到释放,但是无论所卸载的共享库是否真正被释放,传递给dlclose函数的句柄都会在该函数成功返回后立即失效

4:dlerror

char* dlerror(void);
- 功能:获取在加载、使用和卸载共享库过程中所发生的错误
- 返回值:有错误则返回指向错误信息字符串的指针,否则返回NULL
  • 使用
// 演示动态库的动态加载
#include <stdio.h>
#include <dlfcn.h>// dlopen dlsym dlclose dlerror
int main(){
        // 载入内存
        void*  handle = dlopen("./shared/libmath.so",RTLD_NOW);
        if(handle == NULL){
                fprintf(stderr,"dlopen:%s\n",dlerror());
                return -1;
        }
        // 使用库中的函数
        int (*add)(int,int) = dlsym(handle,"add");
        if(add == NULL){
                fprintf(stderr,"dlsym:%s\n",dlerror());
                return -1;
        }
        void (*show)(int,char,int,int) = dlsym(handle,"show");
        if(show == NULL){
                fprintf(stderr,"dlsym:%s\n",dlerror());
                return -1;
        }
        int a=8,b=5;
        show(a,'+',b,add(a,b));
        // 卸载库
        dlclose(handle);
        return 0;
}

在使用上面函数的时候,需要引入dlfcn.h的头文件,在编译时,还需要链接到该库

gcc load.c -ldl

5、虚拟地址

在这里插入图片描述

// 虚拟地址空间的布局
#include <stdio.h>
#include <stdlib.h>
const int const_global = 1;// 常全局变量
int init_global = 2;// 初始化全局变量
int uninit_global; // 未初始化全局变量
int main(int argc,char* argv[],char* envp[]){
	const static int const_static=3;// 常静态变量
	static int init_static = 4;// 初始化静态变量
	static int uninit_static;// 未初始化静态变量
	const int const_loacl = 5;// 常局部变量
	int local;// 局部变量
	int* heap = malloc(sizeof(int));// 堆变量
	char* string = "hello";// 字面值常量
	printf("----------参数和环境区------------\n");
	printf("   命令行参数:%p\n",argv);
	printf("     环境变量:%p\n",envp);
	printf("---------栈区-------------------\n");
	printf("   常局部变量:%p\n",&const_loacl);
	printf("    局部变量:%p\n",&local);
	printf("--------堆区--------------------\n");
	printf("     堆变量:%p\n",heap);
	printf("---------BSS区------------------\n");
	printf("未初始化全局变量:%p\n",&uninit_global);
	printf("未初始化静态变量:%p\n",&uninit_static);
	printf("--------数据区-------------------\n");
	printf(" 初始化全局变量:%p\n",&init_global);
	printf(" 初始化静态变量:%p\n",&init_static);
	printf("--------代码区-------------------\n");
	printf("        函数:%p\n",main);
	printf("    字面值常量:%p\n",string);
	printf("    常全局变量:%p\n",&const_global);
	printf("    常静态变量:%p\n",&const_static);
	printf("----------------------------------\n");
	return 0;
}

结果如下:
在这里插入图片描述

  • 相关函数
    1:mmap
// 头文件 sys/mman.h
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
- 功能:建立虚拟内存到物理内存或磁盘文件的映射
- 参数:
	- start:映射区虚拟内存的起始地址,NULL系统自动选定后返回。
	- length:映射区字节数,自动按页取整。
	- prot:映射区操作权限,可做以下值:
		PROT_READ - 映射区可读
		PROT_WRITE - 映射区可写
		PROT_EXEC - 映射区可执行
		PROT_NONE - 映射区不可访问
	- flags:映射标志,可取以下值:
		MAP_ANONYMOUS - 匿名映射,将虚拟内存映射到物理内存而非文件,忽略fd和offset参数
		MAP_PRIVATE - 对映射区的写操作只反映到缓冲区中并不会真正写入文件
		MAP_SHARED - 对映射区的写操作接反映到文件中
		MAP_DENYWRITE - 拒绝其它文件的写操作
		MAP_FIXED - 若在start上无法创建映射,则失败(无此标志系统会自动调整)
	- fd:文件描述符
	- offset:文件偏移量,自动按页(4K)对齐
- 返回值:成功返回映射区虚拟内存的起始地址,失败返回MAP_FAILED(-1)

2:munmap

// 头文件 sys/mman.h
int munmap(void* start,size_t length);
- 功能:解除虚拟内存到物理内存或磁盘文件的映射
- 参数:
	- start:映射区虚拟内存的起始地址。
	- length:映射区字节数,自动按页取整。
- 返回值:成功返回0,失败返回-1。
munmap允许对映射区的一部分解映射,但必须按页处理
  • 例子
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
int main(){
        // 建立映射
        char* start = mmap(NULL,4096,PROT_READ | PROT_WRITE,MAP_ANONYMOUS | MAP_PRIVATE,0,0);
        if(start == MAP_FAILED){
                perror("mmap");
                return -1;
        }
        // 使用
        strcpy(start,"zpyl");
        printf("%s\n",start);
        // 解除映射
        if(munmap(start,4096)==-1){
                perror("munmap");
                return -1;
        }
        return 0;
}

3:sbrk

// 头文件 unistd.h
void* sbrk(intptr_t increment);
- 功能:以相对方式分配和释放虚拟内存
- 参数:increment堆内存的字节增量(以字节为单位)
	 \> 0 - 分配内存
	 \< 0 - 释放内存
	 \= 0 - 当前堆尾
- 返回值:成功返回调用该函数前的堆尾指针,失败返回-1/* 系统内部维护一个指针,指向当前堆尾,即堆区最后一个字节的下一个位置,sbrk函数根据增量参数调整该指针的位置,同时返回该指针在调整前的位置,其间若发现内存页耗尽或空闲,则自动追加或取消内存页的映射 */

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
int main(){
        printf("当前堆尾:%p\n",sbrk(0));
        int* p=sbrk(4);
        *p = 123;
        printf("p=%p\n",p);
        double* p2 = sbrk(8);
        *p2 = 3.14;
        printf("p2=%p\n",p2);
        printf("%d %lg\n",*p,*p2);
        // 释放
        sbrk(-(4+8));
        printf("当前堆尾:%p\n",sbrk(0));
        return 0;
}

4:brk

// 头文件 unistd.h
int brk(void* end_data_segment);
- 功能:以绝对方式分配和释放虚拟内存
- 参数:end_data_segment:堆尾指针的目标位置
			\> 堆尾指针的原位置 - 分配内存
			\< 堆尾指针的原位置 - 释放内存
			\= 堆尾指针的原位置 - 空操作
- 返回值:成功返回0,失败返回-1/* 系统内部维护一个指针,指向当前堆尾,即堆区最后一个字节的下一个位置,brk函数根据指针参数设置该指针的位置,其间若发现内存页耗尽或空闲,则自动追加或取消内存页的映射*/

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
int main(){
        printf("当前堆尾:%p\n",sbrk(0));
        int* p =sbrk(0);
        brk(p+1);
        printf("p=%p\n",p);
        *p=123;
        double* p2 = sbrk(0);
        brk(p2+1);
        printf("p2=%p\n",p2);
        *p2=3.14;
        printf("%d %lg\n",*p,*p2);
        // 释放 
        brk(p); 
        printf("当前堆尾:%p\n",sbrk(0));
        return 0; 
}

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

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

相关文章

opencascade AIS_InteractiveContext源码学习4 object local transformation management

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是&#xff0c;对于已经被交互上下文识别的交互对象&#xff0c;必须使用上下文方法进行…

数据结构4---串

一、字符串暴力匹配 要注意的就是i与j的回溯&#xff0c;通过不断移动主串的指针&#xff0c;时间复杂度高 #include <stdio.h> #include <stdlib.h>typedef struct String {char* data;int len; }String;String* initString() {String* s (String*)malloc(sizeo…

分布式理论与设计 四、分布式系统设计策略

在分布式环境下&#xff0c;有几个问题是普遍关心的&#xff1a; 如何检测当前节点还活着&#xff1f;如何保障高可用&#xff1f;容错处理负载均衡 1.心跳检测 在分布式环境中&#xff0c;我们提及过存在非常多的节点&#xff08;Node&#xff09;。那么就有一个非常重要的…

c++ 编译过程杂记等

开篇一张图。 编译器 把我们的代码翻译成机器语言 ​ gcc编译程序的过程 gcc编译程序主要经过四个过程&#xff1a; 四个过程说明&#xff1a; ​ 预处理实际上是将头文件、宏进行展开。 编译阶段&#xff0c;gcc调用不同语言的编译器&#xff0c;例如c语言调用编译器ccl…

OpenTenBase入门

什么是OpenTenBase OpenTenBase 是一个提供写可靠性&#xff0c;多主节点数据同步的关系数据库集群平台。你可以将 OpenTenBase 配置一台或者多台主机上&#xff0c; OpenTenBase 数据存储在多台物理主机上面。数据表的存储有两种方式&#xff0c; 分别是 distributed 或者 re…

Android Studio main,xml 视图代码转换

Android Studio main,xml 视图&&代码转换 其实很简单,但是对我们小白来说还是比较蒙的。 废话不多说,直接上图。 我的Android Studio 是 4.0 版的 我刚打开是这个界面,在我想学习如何用代码来布局,可能大家也会找不见代码的位置。 follow me 是不是感觉很简单呢。…

使用Python和BeautifulSoup轻松抓取表格数据

你是否曾经希望可以轻松地从网页上获取表格数据&#xff0c;而不是手动复制粘贴&#xff1f;好消息来了&#xff0c;使用Python和BeautifulSoup&#xff0c;你可以轻松实现这一目标。今天&#xff0c;我们将探索如何使用这些工具抓取中国气象局网站(http://weather.cma.cn)上的…

使用fastapi和pulumi搭建基于Azure云的IAC Restful API服务 — 对外发布

前言 在IAC&#xff08;即Infrastructure As Code&#xff0c;基础设施即代码&#xff09;领域&#xff0c;Terraform 是一个老牌工具&#xff0c;使用HCL&#xff08;HashiCorp Configuration Language&#xff09;语言来编写配置文件。它支持几乎所有主流的云提供商&#xf…

贝锐蒲公英异地组网方案:实现制药设备远程监控、远程运维

公司业务涉及放射性药品的生产与销售&#xff0c;在全国各地拥有20多个分公司。由于药品的特殊性&#xff0c;在日常生产过程中&#xff0c;需要符合药品监管规范要求&#xff0c;对各个分部的气相、液相设备及打印机等进行监管&#xff0c;了解其运行数据及工作情况。 为满足这…

[极客大挑战 2020]Roamphp4-Rceme

rce,rce,rce!!! 右键源代码里给了提示&#xff0c;有备份文件index.php.swp,大伙都做到这来了&#xff0c;应该不用写了吧。看源码 <?php error_reporting(0); session_start(); if(!isset($_SESSION[code])){$_SESSION[code] substr(md5(mt_rand().sha1(mt_rand)),0,5);…

电脑上使用备忘录怎么查看编辑时间?能显示时间的备忘录

在快节奏的生活中&#xff0c;很多人喜欢使用备忘录来记录日常事项和重要信息。备忘录不仅能帮助我们捕捉灵感&#xff0c;还能确保重要任务不被遗漏。然而&#xff0c;有时候我们需要知道某条记录的编辑时间&#xff0c;以便于回溯和整理信息。如果备忘录不能显示编辑时间&…

matplotlib 做饼图

饼图可以很好地帮助用户快速了解整体市场数据的占比分配 import matplotlib.pyplot as pltexplode (0,0.1,0,0) labels Frogs,Hogs,Dogs,Logs sizes [15, 30, 45, 10] fig,ax plt.subplots() # colors 设置图形颜色 ;pctdistance&#xff1a;设置百分比标签与圆心的距离&am…

【配置】Notion自动化备份到github方案

步骤 打开notion网页&#xff0c;获取到需要的值 token_v2 找到请求getSpaces的 Cookie 值 token_v2 space_id 找到请求getSpaces的响应结果space,如下图&#xff1a; file_token 找个页面点击导出&#xff0c;之后拿到这个配置项 注意&#xff1a;配置项会过期&#xff0c…

华为---静态路由-浮动静态路由及负载均衡(二)

7.2 浮动静态路由及负载均衡 7.2.1 原理概述 浮动静态路由(Floating Static Route)是一种特殊的静态路由&#xff0c;通过配置去往相同的目的网段&#xff0c;但优先级不同的静态路由&#xff0c;以保证在网络中优先级较高的路由&#xff0c;即主路由失效的情况下&#xff0c…

计算机毕业设计Python深度学习房价预测 房价可视化 链家爬虫 房源爬虫 房源可视化 卷积神经网络 大数据毕业设计 机器学习 人工智能 AI

基于python一/二手房数据爬虫分析预测系统可视化 商品房数据Flask框架&#xff08;附源码&#xff09; 项目介绍python语言、Flask框架、MySQL数据库、Echarts可视化 sklearn机器学习 多元线性回归预测模型、requests爬虫框架 链家一手房 一手房数据商品房数据、分析可视化预测…

QT中利用QMovie实现动态加载效果

1、效果 2、代码 #include "widget.h" #include "ui_widget.h" #include <QLabel> #include <QMovie>

蓝桥杯-明年再战了

差几名省一(唉唉唉)&#xff0c;ne555定义错类型了&#xff0c;导致后面大部分没写完&#xff0c;检查了一个小时才检查出来.........

【漏洞复现】万户-ezOFFICE DownloadServlet 任意文件下载漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

100多个ChatGPT指令提示词分享

当前&#xff0c;ChatGPT几乎已经占领了整个互联网。全球范围内成千上万的用户正使用这款人工智能驱动的聊天机器人来满足各种需求。然而&#xff0c;并不是每个人都知道如何充分有效地利用ChatGPT的潜力。其实有许多令人惊叹的ChatGPT指令提示词&#xff0c;可以提升您与ChatG…

stm32学习-硬件I2C读取MPU6050

配置流程 第一步&#xff1a;配置I2C外设&#xff0c;对I2C外设进行初始化&#xff08;替换上一篇文章的I2C_Init&#xff09; 第二步&#xff1a;控制外设电路&#xff0c;实现指定地址写的时序&#xff08;替换上一篇文章的WriteReg&#xff09; 第三步&#xff1a;控制外…