基础IO(下)

模拟实现封装C标准库

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#define NUM 1024
#define NONE_FLUSH 0x0 //无缓冲
#define LINE_FLUSH 0x1 //行缓冲
#define FULL_FLUSH 0x2 //全缓冲
typedef struct My_FILE
{
    
    
  int _fileno;
  char _buffer[NUM];
  int _end;
  int _flags;//flush method
}My_FILE;
My_FILE* my_fopen(const char* filename, const char* method)
{
    
    
  assert(filename);
  assert(method);
  int flags = O_RDONLY;
  if(strcmp(method, "r") == 0)
  {
    
    

  }
  else if(strcmp(method, "r+") == 0)
  {
    
    
    
  }
  else if(strcmp(method, "w") == 0)
  {
    
    
    flags = O_WRONLY | O_CREAT | O_TRUNC;
  }
  else if(strcmp(method, "w+") == 0)
  {
    
    
    
  }
  else if(strcmp(method, "a") == 0)
  {
    
    
    flags = O_WRONLY | O_CREAT | O_APPEND;
  }  
  else if(strcmp(method, "a+") == 0)
  {
    
    

  }  
  int fileno = open(filename, flags, 0666);
  if(fileno < 0)
  {
    
    
    return NULL;
  }
  My_FILE* fp = (My_FILE*)malloc(sizeof(My_FILE));
  if(fp == NULL)
  {
    
    
    return fp;
  }
  memset(fp, 0, sizeof(My_FILE));
  fp->_fileno = fileno;
  fp->_flags |= LINE_FLUSH;
  fp->_end = 0;
  return fp; 
}
void my_fwrite(My_FILE* fp, const char* start, int len)
{
    
    
  assert(fp);
  assert(start);
  assert(len > 0);
  strncpy(fp->_buffer + fp->_end, start, len); //将数据写入到缓冲区
  fp->_end += len;
  if(fp->_flags & NONE_FLUSH)
  {
    
    }
  else if(fp->_flags & LINE_FLUSH)
  {
    
    
    if(fp->_end > 0 && fp->_buffer[fp->_end - 1] == '\n')
    {
    
    
      write(fp->_fileno, fp->_buffer, fp->_end);
      fp->_end = 0;
      
      syncfs(fp->_fileno);//将数据写到磁盘中
    }
  }
  else if(fp->_flags & FULL_FLUSH)
  {
    
    }
  
}
void my_fflush(My_FILE* fp)
{
    
    
  assert(fp);
  if(fp->_end > 0)
  {
    
    
    write(fp->_fileno, fp->_buffer, fp->_end);
    fp->_end = 0;
    syncfs(fp->_fileno);//将数据写到磁盘中
  }

}
void my_fclose(My_FILE* fp)
{
    
    
  my_fflush(fp);
  close(fp->_fileno);
  free(fp);
}
int main()
{
    
    
  My_FILE* fp = my_fopen("log.txt", "w");
  if(fp == NULL)
  {
    
    
    printf("my_fopen error\n");
    return 1;
  }
  const char* s = "hello file";
  my_fwrite(fp, s, strlen(s));
  printf("写入了不满足刷新条件的字符串\n");
  fork();
  my_fclose(fp);
  return 0;
}

运行截图:

image-20221025193000461

为shell添加重定向功能

代码:

#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<assert.h>
#include<ctype.h>
#define NUM 1024
#define SIZE 128
#define SEP " "
#define ENV_MAXNUM 20//限定可以导入环境变量数目的最大值
#define NONE_REDIR -1
#define INPUT_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2
#define DROP_SPACE(start) do{
      
      while(isspace(*start)) start++;}while(0);
  int g_redir_flag = -1;
  char*g_redir_filename = NULL; 
  char env_buffer[ENV_MAXNUM][NUM];//存储环境变量
  char command_line[NUM];
  char* command_args[SIZE];
  extern char** environ;
  int Change_args(char* newPath)
  {
    
    
    chdir(newPath);
    return 0;
  }
  void PutEnv(char* newEnv)
  {
    
    
    putenv(newEnv);
  }
  void CheckDir(char* commands)
  {
    
    
    assert(commands);
    char* start = commands;
    char* end = commands + strlen(commands);//end解引用就是'\0'
    while(start < end)
    {
    
    
      if(*start == '>')
      {
    
    
        if(*(start+1) == '>')//说明此时是追加重定向
        {
    
    
          *start = '\0';
          start++;
          *start = '\0';
          g_redir_flag = APPEND_REDIR;
          start++;
          DROP_SPACE(start);
          g_redir_filename = start;
        }
        else//说明此时是输出重定向 
        {
    
    
          *start = '\0';
          g_redir_flag = OUTPUT_REDIR;
          start++;
          DROP_SPACE(start);
          g_redir_filename = start;
        }
        break; 
      }
      else if(*start == '<')//此时是输入重定向
      {
    
    
        *start = '\0';
        g_redir_flag = INPUT_REDIR;
        start++;
        DROP_SPACE(start);
        g_redir_filename = start;
        break;
      }
      else 
      {
    
    
        start++;
      }
    }
  }
  int main()
  {
    
    
    static int count = 0;//记录环境变量的起始下标
    //shell本质上就是一个死循环
    while(1)
    {
    
    
      g_redir_flag = -1;
      g_redir_filename = NULL;
      //不关心获取这些属性的接口
      //1.显示提示符
      printf("[zs@VM-0-3-centos 当前目录]# ");
      fflush(stdout);
      //2.获取用户输入 
      memset(command_line,'\0',sizeof(command_line)*sizeof(char));
      fgets(command_line, NUM, stdin);//从标准输入中获取的,获取到的是C风格的字符串
      command_line[strlen(command_line)-1] = '\0'; 
      //处理重定向相关操作
      //ls -a -l>log.txt / cat<log.txt / ls -a -l>>log.txt
    CheckDir(command_line);
    //3.分割字符串 "ls -a -l" "ls" "-a" "-l"
    command_args[0] = strtok(command_line, SEP);
    int index = 1;
    //4.TODO,处理内建命令
    if(strcmp(command_args[0], "ls") == 0) 
      command_args[index++] = (char*)"--color=auto";
    while(command_args[index++] = strtok((char*)NULL, SEP));
    //处理cd内建命令
    if(strcmp(command_args[0], "cd") == 0 && command_args[1] != NULL)
    {
    
    
      Change_args(command_args[1]);
      continue;
    }
    //处理内建命令export
    if(strcmp(command_args[0], "export") == 0 && command_args[1] != NULL)
    {
    
    
      //目前环境变量信息在command_line中,会被清空
      //此处我们需要自己保存一下环境变量的内容
      strcpy(env_buffer[count], command_args[1]);
      PutEnv(env_buffer[count++]);//将command_args[1]导入到环境变量中
      continue;
    }
    //5.创建进程,执行
    
    pid_t id = fork();
    if(id == 0)
    {
    
    
      int fd = -1;
      switch(g_redir_flag)
      {
    
    
        case NONE_REDIR:
          break;
        case INPUT_REDIR:
          fd = open(g_redir_filename, O_RDONLY);
          dup2(fd, 0);
          break;
        case OUTPUT_REDIR:
          fd = open(g_redir_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
          dup2(fd,1);
          break;
        case APPEND_REDIR:
          fd = open(g_redir_filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
          dup2(fd,1);
          break;
        default:
          printf("Bug!\n");
          break;
      }
      //child
      execvp(command_args[0], command_args);
      exit(-1);//执行到这里,子进程一定替换失败了
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret>0)
    {
    
    
     printf("等待子进程成功:sig:%d,code:%d\n", status&0x7F, (status>>8)&0xFF); 
    }
  }//end while


  return 0;
}

区分stdout和stderr

注意:C++中后缀不只可以是.cpp还可以是.cc、.cxx

代码:

#include<iostream>
#include<cstdio>
int main()
{
    
    
  //stdout
  printf("hello printf 1\n");
  fprintf(stdout, "hello fprintf 1\n");
  fputs("hello fputs 1\n", stdout);

  //stderr
  perror("hello perror 2\n");
  fprintf(stderr, "hello fprintf 2\n");
  fputs("hello fputs 2\n", stderr);

  //cout
  std::cout << "hello cout 1" << std::endl;
  //cerr
  std::cerr << "hello cerr 2" << std::endl;

  return 0;
}

运行截图:

image-20221026141436150

image-20221026141622961

我们看到当我们进行重定向输出到log,.txt文件的时候只是将stdout中的内容重定向到了log.txt文件中,那么我们该如何将stderr中的内容也重定向到文件中呢?

image-20221026145631011

注意:stdout和stderr虽然都是通过显示器进行打印,但他们始终都是不同的:

image-20221026150542631

image-20221026150532449

那么上面操作的意义是什么呢?

可以区哪些是日常输出,哪些是错误,进而更好的查看错误信息和定位错误地址。

问:如果我们想要将两者都混合起来输出到一个文件中,该如何操作呢?

答:image-20221026151433539

image-20221026151659201

问:上面的:Success是什么?

答:C语言中有一个全局变量errno,记录最近一次库函数调用失败的原因。

image-20221026152755711

文件系统

管理磁盘中文件的系统叫做文件系统。

磁盘的物理结构

磁盘是我们电脑上唯一的一个机械设备。当然,我们电脑上目前用的更多的是机械硬盘和固态硬盘。

问:磁盘是如何存储数据的?

答:磁盘是有磁性的,一般是通过N/S即来进行区分0/1的。

image-20221026170611259

image-20221026170740378

磁盘的存储结构

image-20221026174309557

image-20221026171523808

image-20221027144213903

注意:磁盘访问的基本单位是扇区,但并不是说明访问磁盘必须按照一次访问一个扇区来进行访问,有可能是一次访问多个扇区

磁盘的逻辑结构

把盘片想象成为线性结构:

image-20221027145444076

那么LBA和CHS是如何进行转换的呢?

下面进行一个简单的举例:

假设总共有4000个扇区,4个扇面(不考虑正反面),一个扇面有20个磁道(一个磁道有50个扇区),

LBA = 3234

H(磁面):3 <= 3234/1000

C(磁道):11 <= 3234 % 1000 / 20

S(扇区):14 <= 3234 % 1000 % 20

文件系统的管理

image-20221027164244088

image-20221028104601700

问:为什么Super Block要设计在组内,而不是组外,既然管理的是整个分区,不应该是设计在组外更加合适吗?

答:Super Block分布在多个组中是为了多份备份,防止Super Block文件损毁时整个分区都将无法使用。

注意:不是所有块组中都有Super Block,可能只有两三个,毕竟存在多份的意义就是为了备份而已,没必要每个块组都进行存储,况且每个块组都存储一份Super Block的话也会对修改文件的效率有很大的影响。

问:一个inode(文件,属性)如何和属于自己的内容关联起来呢?

答:

struct inode
{
     
     
 //文件的所有属性
 blocks[15];
 //0~11保存的就是该文件对应的Data blocks编号
 //12~14指向一个Data blocks,但是这个Data blocks并不保存有效数据,而保存该文件所使用的其它块的编号
}

只要我们能够找到文件的inode就能找到文件的内容和属性。

问:文件名是如何和文件的inode对应起来的呢?

答:文件名也算文件的属性,但是某个特定的文件的inode里并不保存该文件的文件名,但是Linux底层实际都是通过inode编号标识文件的,那么inode是如何找到的呢?我们前面已经知道,目录也是文件,目录的数据块中存储的就是该目录下文件名和inode的映射关系(文件名是key,inode是value,这也说明了为什么不能在一个目录下多个同名文件的原因)。这也印证了为什么创建一个文件需要我们对该目录具有w权限,查看一个文件名需要我们对该目录具有r权限。

理解创建文件/删除文件/查看文件

创建一个文件的本质

OS在创建一个文件时,先在特定的块组中查找inode Bitmap,找到一个未被使用的inode,将其比特位由0置1,然后将新创建的文件属性写到对应的inode Table中,可以将struct inode的blocks数组全部置为0,当我们向文件中写入内容时,就先通过Block bitmap查看哪个Data blocks未被使用,然后在写入文件的同时,将未被使用的Data blocks编号写到struct inode的blocks数组中同时修改Block bitmap由0置1。当然,最关键的是:将文件名和对应的inode写入到创建文件所处的目录文件的数据块(Data blocks)中。

删除一个文件的本质

1、删除映射关系:先知道该目录文件的文件名,然后根据文件名找到该目录文件的inode,然后通过inode找到该目录文件的数据块(Data blocks),然后将该数据块中存储的要删除的文件名和inode的映射关系删除掉

2、修改inode Bitmap和Block Bitmap:通过要删除的文件的inode找到使用的blocks,然后将对应的Block Bitmap由1置0即可,然后将对应的inode Bitmap中相对应的inode比特位由1置0。(这个修改位图发生在1中删除映射关系之前的)

相关扩展

问:我们知道自己当前所处的目录,就能知道目录的inode吗?

答:不能。以下面的路径进行举例:

image-20221028123722042

我们知道当前所处的目录即2022_10_28,如果我们想要知道当前目录2022_10_28的inode,就必须来到linux_for_practice目录,linux_for_practice目录中的Data blocks块中保存的文件内容就是2022_10_28的目录名和inode映射。

所以我们就能够推导处ls指令底层到底做的是什么:比如在上面的路径下执行ls指令,首先先通过…上级目录的Data Blocks找到当前目录即2022_10_28的inode,然后通过inode找到当前目录的Data Blocks,进而找到文件名和inode的映射关系,然后将文件名输出到stdout上即可。

查看文件的更多属性

命令:stat filename

image-20221028133458869

三个时间:

  • Access 最后访问时间
  • Modify 文件内容最后修改时间
  • Change 属性最后修改时间

软硬链接

软链接

指令:ln -s 被链接文件名 链接文件文件名

示例:

image-20221028142742924

作用:软链接经常会被作为一种类似Windows下的快捷方式的形式帮助我们快速的执行某个文件。

内容:保存的是指向的文件的所在路径。

硬链接

链接指令

指令:ln 被链接文件名 链接文件名

示例:

image-20221028143107033

作用:在Linux指定的目录下,给指定的文件新增文件名和inode编号的映射关系。一般用来进行路径间的切换。

图示理解:wKioL1lvUGjQ8TirAAAnGNq3aDs576.png

硬链接数

硬链接数记录了有几个硬链接本质就是该文件inode的一个计数器count,标识有几个文件名和该文件的inode建立了映射关系。简而言之,就是有几个文件名指向该文件的inode。

示例:

image-20221028150217373

问:为什么目录文件(dir)创建出来硬链接数是2,普通文件创建出来硬链接数是1?

答:普通文件创建出来硬链接数是1的原因很好理解,因为普通问及那的文件名本身就映射着该文件inode,且只有一份。

目录文件在该目录内默认存在一个文件名叫.的隐藏文件表示当前目录,该文件也是该目录所对应目录文件的映射。且如果该目录文件内还有目录文件(indir),那么在indir路径的时候,会存在一个文件名叫..的隐藏文件表示上级目录,该文件就是dir目录文件的硬链接文件,此时硬链接数还要加1,变成3。

软硬链接的区别

软链接是独立的文件,有自己的inode编号,硬链接不是一个独立的文件,和目标文件使用的是同一个inode。

问:软链接很 明显是占据磁盘空间的,那么硬链接是否占据磁盘空间呢?

答:硬链接是不占据磁盘空间的,但既然如此,那为什么我们使用ls -h指令时发现计算得出所占磁盘空间的总数也是包含硬链接文件的呢?因为ls -h这条命令进行统计文件总大小的时候,并不是从磁盘进行统计的,而是根据文件属性的大小进行相加然后得到的最后的结果。

删除链接的方式

unlink 链接文件名:unlink的作用其实和rm是类似的,作用结果也是完全一样的,但是我们一般会习惯使用unlink来删除链接文件,而不使用rm来进行删除。

举例如下:

unlink myhard.txt

unlink mysoft.txt

静态库和动态库

概念

  • 静态库(.a):程序在编译链接的时候把库的代码链接(拷贝)到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚 拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

设计静态库和动态库

设计静态库

mymath.h文件:

#pragma once
#include<stdio.h>
#include<assert.h>
//[from, top] 累加 -> result -> return
extern int addToVal(int from, int to);

mymath.c文件

#include"mymath.h"
int addToVal(int from, int to)
{
    
    
  assert(from <= to);
  int result = 0;
  for(int i = from; i <= to; i++)
  {
    
    
    result += i; 
  }
  return result;
}

myprint.c文件

#include"myprint.h"
void Print(const char* msg)
{
    
    
  printf("%s:%lld\n", msg, (long long)time(NULL));
}

myprint.h文件:

#pragma once
#include<stdio.h>
#include<time.h>
extern void Print(const char* msg);

形成静态库:

makefile文件

命令形式:ar -rc xxxx.a xx1.o xx2.o(xxxx.a的格式必须是libxxx.a的格式)

ar是gnu归档工具,rc表示(replace and create)

Makefile文件

libmymath.a:mymath.o myprint.o
	ar -rc libmymath.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:clean
clean:
	rm -f *.o *.a

下面对上面的Makefile文件进行优化,使其能够生成一个产品:

libmymath.a:mymath.o myprint.o
	ar -rc libmymath.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:static
static:
	mkdir -p lib-static/lib
	mkdir -p lib-static/include
	cp *.a lib-static/lib 
	cp *.h lib-static/include
.PHONY:clean
clean:
	rm -rf *.o *.a lib-static 

使用Makefile截图:

image-20221029135408682

设计动态库

  • shared: 表示生成共享库格式

  • fPIC:产生位置无关码(position independent code)

    静态库:.o文件的代码不能在内存的任意位置去加载,必须拷贝进我们的程序中以地址空间绝对地址的方案呈现在进程层面上让系统去调用,这叫与地址有关码。

    动态库:.o代码代码可以加载到内存空间的任何位置,都能够让我们的程序调用并执行它。采用的是起始地址+偏移量的方式即相对地址的方案。

  • 库名规则:libxxx.so

Makefile文件:

libmymath.so:mymath.o myprint.o 
	gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o:mymath.c
	gcc -fPIC -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -fPIC -c myprint.c -o myprint.o
.PHONY:dyl
dyl:
	mkdir -p lib-dyl/lib
	mkdir -p lib-dyl/include
	cp *.so lib-dyl/lib 
	cp *.h lib-dyl/include
.PHONY:clean
clean:
	rm -rf *.o *.so lib-dyl

使用图示:

image-20221029142656947

同时设计动态库和静态库

Makefile文件:

.PHONY:all
all:libmymath.so libmymath.a

libmymath.so:mymath.o myprint.o 
	gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o:mymath.c
	gcc -fPIC -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -fPIC -c myprint.c -o myprint.o

libmymath.a:mymath_s.o myprint_s.o
	ar -rc libmymath.a mymath_s.o myprint_s.o
mymath_s.o:mymath.c
	gcc -c mymath.c -o mymath_s.o -std=c99
myprint_s.o:myprint.c
	gcc -c myprint.c -o myprint_s.o

.PHONY:lib
lib:
	mkdir -p lib-static/lib
	mkdir -p lib-static/include
	cp *.a lib-static/lib 
	cp *.h lib-static/include
	mkdir -p lib-dyl/lib
	mkdir -p lib-dyl/include
	cp *.so lib-dyl/lib 
	cp *.h lib-dyl/include
.PHONY:clean
clean:
	rm -rf *.o *.a *.so *lib

使用图示:

image-20221029152503093

使用静态库和动态库

头文件的搜索路径:

  • “” or <>

    1、在当前路径下查找头文件

    2、在系统头文件路径下查找头文件(/usr/include/)

  • 指定头文件的搜索路径:gcc Test.c -I ./lib-static/include/

库的搜索路径:

/lib64/路径下。

使用静态库

  • 将我们的头文件和库添加到系统的库中去(不推荐,会污染库)

    将头文件和静态库文件拷贝到系统的头文件路径和系统的静态库路径下(这个过程我们也一般叫作库的安装):

    image-20221029155832985

    此时的报错就不是头文件找不到了,而是方法没有定义了,因为gcc/g++默认知道使用/lib64/里面的标准库,但是我们刚cp进去的库文件,系统是不知道的(因为他们不是标准库),我们需要在gcc选项中自己手动加上:

    gcc 源文件 -l库名    
    //此处需要注意的是,库名是去掉前面的lib和后面的.a后缀的,比如这个地方我们的库名就是mymath(原名:libmymath.a)
    //-l 指明要链接的第三方库的名称
    

    image-20221029160222690

    此时能够说明我们链接添加的是静态库的个具体表现就是当我们删掉我们添加到库中的头文件和库文件(卸载库)后,我们刚刚形成的a.out文件依然能够继续执行:

    image-20221029161110334

  • 指定头文件搜索路径和库文件搜索路径

    gcc Test.c -I ./lib-static/include/
    

    image-20221029162314083

    gcc Test.c -I ./lib-static/include/ -L ./lib-static/lib/ -lmymath  
    //注意:-I:头文件搜索的路径  -L:库所在的路径  -l:后面紧跟在L指定的路径下我们想要链接的确切的库的名字(当然,可以加空格,可以不加,不推荐加)
    

使用动态库

  • 将我们的头文件和库拷贝到系统的库中去(动态库放到/lib64/或者/usr/local/lib64/目录下,头文件还是/usr/include/下,不推荐,会污染库)

  • 指定头文件搜索路径和库文件搜索路径

    gcc Test.c -I lib-dyl/include/ -L lib-dyl/lib/ -l mymath
    

    image-20221029171452156

    使用上面的方式生成a.out可执行程序后发现无法执行,此时我们通过ldd a.out查看一下程序依赖的库:

    image-20221029171618269

    发现依赖的动态库没有查找到。

    此时我们思考一下为什么,我们之前用的选项-I -L -l都是告诉的gcc编译器在哪里链接什么库,并且gcc编译器已经帮助我们生成了可执行程序,既然已经生成了可执行程序,那么就和gcc编译器已经没有任何关系了,程序一旦(./a.out)运行起来就变成了进程,此时没有人告诉这个进程该去哪链接库,所以在编译生成可执行程序时告诉编译器去哪链接库生成可执行程序和告诉进程去哪里进行链接库是不一样的,这里也体现出了动态库和静态库链接的不同,静态库一经链接生成可执行程序在运行可执行程序的时候就不需要再进行链接了(静态库在形成可执行程序时就把需要的静态库拷贝到代码中了,所以运行时就不需要链接任何库了),但是动态库即使生成了可执行程序在运行可执行程序的时候依旧需要有人去告诉它去哪链接库(程序和动态库是分开加载的)。

    那么,如何使进程找到动态库呢?

    • 动态库拷贝到系统路径下

    • 通过导入环境变量的方式 – 程序运行的时候,会在环境变量中查找自己需要的动态库路径 —— LD_LIBRARY_PATH(关机退出后再重新进入就不行了,只在当前命令行中有效)

      最开始的环境变量:

      image-20221029174629697

      添加后:

      image-20221029174504094

      此时再通过ldd指令查看a.out文件的依赖路径:

      image-20221029174818633

      此时程序就可以运行成功了

      image-20221029174529647

    • 系统配置文件(/etc/ld.so.conf.d/该路径表明了系统在扫描动态库路径时出了在系统路径下进行扫描外,还会在该路径的文件中存储的路径下进行扫描)(关掉之后再打开依旧有效)

      注意:该路径下存储的就是路径。下面进行查看:

      image-20221029175839180

      /etc/ld.so.conf.d/路径下新建一个文件名叫myPath并向其写入动态库所在路径(都需要sudo来提升权限)

      image-20221029204748643

      使用ldd a.out命令发现依然显示not found

      image-20221029204106664

      此时执行下面的命令需要让刚才添加的路径生效:

      sudo ldconfig 
      

      再次执行ldd a.out进行查看:

      image-20221029204854233

      此时程序可以正常执行了。

    • 其它方式

      在系统路径下建立一个我们自己库文件的软链接

      image-20221029210256082

      此时程序可以正常执行:

      image-20221029210433472

      此时将a.out删除之后再通过gcc生成可执行程序的时候就不需要指明库在哪里了。

      image-20221029210649073

了解动态库在虚拟地址空间中的位置

image-20221029212808559

此时其它的进程也可以将libmymath.so映射进自己struct mm_struct的共享区中,需要注意的是:此时动态库只需要在内存中有一份就可以。

与静态库的不同:

静态库一般会在编译的时候将代码拷贝到代码区中,而动态库是在运行的时候将内存中的动态库代码通过页表映射到共享区中

推荐库

ncurses —— Linux下的图形界面库,安装指令:sudo yum install -y ncurses-devel

boost,安装指令:sudo yum install -y boost-devel

猜你喜欢

转载自blog.csdn.net/m0_57304511/article/details/128890598
今日推荐