Linux服务器开发实战

一 GCC

1 概念

 可以在VS code里面进行终端调试

 编译c文件:生成的app是目标文件

执行程序:./app

 如果直接编译c文件不给目标名称,默认生成a.out(a是名称 out是后缀)

注意:Linux中可执行程序为绿色

2 编程语言的发展

3 GCC工作流程 

预处理:将头文件复制到源代码中、删除注释、宏替换等

可执行程序:windows  .exe  Linux .out

流程展示:

        1 将源代码-E预处理生成预处理后的源代码:gcc test.c -E -o test.i

        2 将预处理后的源代码-S编译生成汇编代码:gcc test.i -S -o test.s

        3 将汇编代码通过-c汇编生成目标代码:gcc test.s -c -o test.o

        4 将目标代码进行链接生成可执行程序:./test.o

注意:也可以gcc test.c变成可执行程序,也会包含前面四个阶段

4 GCC常见参数选项

gcc test.c -o test -D 宏名

5 g++和gcc的区别

 一样的效果

1 将c文件生成可执行程序test:gcc test.c -o test

2 生成app需要test.c:gcc -o app test.c

二 静态库

1 概念

2 静态库的制作

案例:想要将加减乘除打包成静态库(JTK)供别人使用

查看当前文件夹的目录数:tree

步骤:

1 利用-c获得.o文件  :gcc -c add.c sub.c mult.c div.c (main和h文件不需要)

2 创建静态库(JTK)

3 静态库的使用

Linux中文件夹表示为蓝色

src 源代码文件

lib 库文件

步骤:

1 可以在library中重新制作一个静态库或者把上面我们已经在calc文件夹的静态库拷贝过来

拷贝:cp 想要复制文件路径   复制到的文件路径

上一级../目录下的calc文件中的libJTK.a拷贝到这一级./目录下library文件下的lib目录中

注意:分发的时候一定要把头文件和静态库给别人,否则无法使用

2 编译main.c

如果直接对main进行编译的话,由于h头文件不在main中所以会报错

解决方法:

1 将h头文件移到main中

2 采用GCC的参数命令:在末尾加 -I 头文件路径(由于头文件和main在同一级也就是当前文件)

解决了头文件的报错发现add 类的c文件是未定义的使用(因为在编译main的时候只有头文件和库,但库里面的函数没有找到)

解决方法:GCC参数命令 -l 库名称 -L 库路径 (一定要指定库名称,可能有多个库)

易错:

        库名称:JTK

        库文件名称:libJTK.a

三 动态库(共享库)

1 动态库的制作

递归拷贝:文件夹之间的拷贝 cp 文件名 拷贝到的文件夹路径 -r

将JTK文件夹中的calc和library拷贝到DTK文件夹中: 

现在在JTK文件夹中输入命令:cp calc library ../DTK -r

删除文件夹:rm -rf 想删除的文件

动态库制作步骤:

1 通过gcc -fpic生成与位置无关的代码   gcc -c -fpic add.c sub.c mult.c div.c 

2 通过gcc -shared 得到动态库    gcc -shared add.o sub.o mult.o div.o -o libDTK.so

2 动态库的使用

1 把上面制作的动态库拷贝到library下或者自己制作一个动态库

2 编译main文件生成可执行程序

会出现和静态库一样的问题:头文件报错、加减乘除c文件未定义的使用

报错!  动态库加载失败(因为运行的时候,可执行程序找不到动态库的代码)

四 动态库加载失败

1 动态库加载失败原因(原理)

注意:程序都是放在内存中运行的,所以动态库也要加载到内存中

检查可执行程序的依赖关系:ldd  可执行程序名称

解决方法:动态载入器 ld-linux.so

2 解决动态库加载失败问题

可以把终端看成一个程序,ls命令会被shell解析器解析,shell解析器怎么找到命令的路径?环境变量

查看环境变量:env(键值形式)

获取动态库的路径:最后所在位置 pwd

两种方法:

1 方法一:终端配置

1 配置环境变量     export 环境变量名称=$获取环境变量的内容:动态库路径

配置环境变量的内容然后拼接动态库的路径

2 查看环境变量  echo  $环境变量名称

3 tree

4 查看可执行程序的依赖关系  ldd 可执行程序名称

之前no found

5 动态库加载成功可以运行程序

总结:在终端解决动态库加载失败的问题,在关闭终端之后这个配置的环境变量也就失效了

2 方法二 :永久配置

1 用户级别配置  .bashrc

bashrc:配置文件

回到home目录:cd

拓展:vim编辑器跳转最后一行:shift+g

           vim编辑器保存并退出::wq

步骤:

1 进入vim 编辑器 .bashrc 里面配置  export 环境变量名称=$获取环境变量的内容:动态库路径

2 使得vim的修改生效

       1  . . bashrc

       2  source  .bashrc

3 动态库加载成功

2 系统级别配置

拓展:家目录  ~/

加sudo权限 不推荐使用

五 静态库与动态库的对比 

1 程序编译成可执行程序的过程

2 静态库的制作过程

3 动态库的制作过程

4 静态库的优缺点

5 动态库的优缺点

 速度:因为静态库已经加载到程序中,程序一启动就调用静态库,但是动态库只有加载的时候才调用,所以速度相比静态库要慢一点。

六 Makefile

1 概念

 红色的表示压缩文件

2 Makefile文件的命名和规则

步骤:

1 由Vim进入到Makefile文件

2 在Makefile文件中写入

3 执行make命令,生成app 最后执行

Makefile有什么作用?

答:如果不用make的话直接在Linux中输入命令gcc c文件 -o 可执行文件名称即可,可以看出使用Makefile更加繁琐,但是如果我们更改了C文件中的某一个或者多个就要重复输入gcc c文件 -o 可执行文件名称的命令,如果利用make的话就不需要更改了,会大大减少复杂度。

3 工作原理

1 检查依赖

例:

一个Makefile下面有很多的规则,在执行红色部分的命令的时候,会先去查找依赖存不存在,如果存在就执行,如果不存在就向下继续寻找

 2 检测更新

例:

Makefile

Makefile1

Makefile比Makefile1简单,所用的时间也短,如果两者同时存在的时候,不会执行Makefile1,会显示app已是最新,如果现在更改其中c文件中的一个,就会更新一下那个c文件

4 变量

例子:改写

定义变量:

        src 所有依赖文件

        target 可执行文件    

注意:这个改写只是把一行app的规则改了,下面的还是要写,增加了复杂度

解决方法:模式匹配

5 模式匹配

原始:

改写后:

        $< 第一个依赖名称

        $@ 目标名称

利用通配符减少后面四段的重复写法,那么变量的获取能否简写?

解决:函数

6 函数

1 找到所有c文件 $(wildcard  所寻找的c文件路径  可多个参数)

2 找到所有o文件   $(patsubst %.c, %.o ,所有被替换文件)

例子:

原始:

改写:

7 清除 

Makefile中如果加入清除指令,他是没有依赖的,可以理解为无条件成立,所以永远都是最新的。如果在Makefile以外的地方创建一个clean,则不会执行。

由于Makefile里面的clean是无条件成立,永远都是最新的,那么也就意味着在Makefile以外的地方不会再生成clean文件和命令,这种情况我们可以将clean定义为伪目标。

8 伪目标:   .PHONY:伪目标名称

七 GBD调试

1 概念

2 准备工作

调试:gcc test.c -o test -g

3 GDB命令

例子:调试和正常执行的区别

调试:gcc test.c -o test -g  生成test

执行:gcc test.c -o test   生成test1

对比文件大小:

发现经过调试的test比未经调试的test1会更大一点,说明加入了调试信息

命令:

1 启动GDB   gdb 调试文件名称

如果test程序里面main函数由两个参数,执行程序可以 ./teat 参数1 参数2

这是在Linux命令界面操作,如果在GDB调试中如何使用?

2 给程序设置参数  set args 参数1 参数2 ....

3 获取设置参数      show args 

一般23结合使用

   注意:Ctrl + L 清屏指令

4 退出GDB   quit  or q

GDB调试结束后退出回到shell解析器终端

5 查看GDB 使用手册   help

命令很多,回车展现下一屏

6 查找特定命令  help 命令名称

7 查看当前文件代码

查看当前文件代码由两种方式

        1 vim编辑器中查看,没有显示行数    :set nu

       

注意:vim编辑器中查看 vim test.c 如果是vim test 会出现乱码(因为test是可执行程序,我们看的是代码)而且设置行数的时候不是insert是命令

        2 可以利用GDB调试工具命令实现

注意:利用方式二的时候要先进行生成具有调试信息的test  gcc teat.c -o test -g,否则进行GDB会报错,生成调试信息的test的前提是test.c文件和test必须在一起

1 从默认位置显示  list or l

一般是默认打开main所在的文件的代码,多次list或者enter显示完全

2 从当前文件指定的行显示   list 行号
3 从当前文件指定的函数显示 list 函数名

对于cpp文件来说一定要用g++编译,否则找不到C++的库,报错

把cpp的源文件编译成带调试信息的可执行文件main

如果是在此文件下查看其他文件的代码 

8 查看非当前文件的代码

1  指定文件名   list 文件名:行号

2 指定函数名   list 文件名:函数名

list默认显示十行代码,但是可以自定义

9 设置显示的行数 

1 默认十行 show list  or show listsize

2 自定义  set listsize 15

4 GDB命令——断点操作

5 GDB命令——调试命令

八 标准C库IO函数和Linux系统IO函数对比

I    input            O  output

以文件的角度:从内存写东西到文件中称为输入,从文件中读取信息到内存中称为输出

以内存的角度:从文件中读取数据到内存中称为输入,从内存中把数据写到文件中称为输出

一般我们按照内存角度考虑,因为程序在内存中运行

1 标准C库IO函数

注意:标准C库IO函数可以跨平台,可以在Windows和Linux系统下运行

跨平台运行的两种方式:

        1 不同平台有不同的虚拟机

        2 比如要使用C库中的fopen函数,Windows平台要使用就调用Windows系统的API LInux              就调用Linux系统API

文件描述符:指向一个已经打开的文件,Windows中称为文件句柄

IO函数帮助文档   man 3

缓冲区的作用:

答:缓冲区可以提高执行程序的效率,举个例子,要想把数据写入磁盘有两种方式,方法一:写一次运一次到磁盘中,方法二:先把数据写到缓冲区中,之后依次写到磁盘中,明显发现方法二更适合,因为方法一频繁的操作硬件磁盘会更慢。当缓冲区满了、关闭文件或者强制调用fflush都可以向磁盘中写数据。(降低写数据的次数)缓冲区默认大小 8k=8192b

标准C库IO函数和Linux系统的IO函数的区别:

答:标准C库IO函数是带有缓冲区的,Linux系统IO函数是没有缓冲区的,调用一次write就会写入一次磁盘,调用一次read就会读一次磁盘

例子:

1 如果进行网络通信,是需要追求效率的,如果给对方发数据,结果数据还在缓存区,效率太慢,需要选用Linux系统的IO函数

2 如果对磁盘进行读写操作的话,需要缓冲区提高效率,需要调用标准C库的IO函数

2 标准C库IO和Linux系统IO函数的关系  调用与被调用

3 虚拟地址空间

注意:虚拟地址空间是不存在的,是程序员想象出来的

程序:磁盘上的代码

进程:运行中的程序,会加载到内存中

有一个进程就会有一个虚拟地址空间,它的大小由电脑的cpu决定,32位大概是2

的32次方大概4G

内存管理单元 MMU

4 文件描述符

1 可执行文件是不占用内存的,他在磁盘里面,但是进程也就是开始运行的可执行程序是占内存的

每个进程里面有1024个文件描述符,也就是说一个进程里面最多能打开1024个文件

2 PCB   process control break  进程控制块

3 在Linux中,一切皆文件

4 一般来说文件描述符是指向一个文件的,但是指向终端的时候,可以把终端看成一个设备文件

5 一个文件可以被打开多次,返回的文件描述符也是不一样的,被close释放之后可以重新使用文件描述符,找最小的文件描述符来使用

九 Linux系统IO函数

手册:

Linux系统IO函数  man 2 函数名

标准C库IO函数    man 3 函数名

系统调用   system call 

1 open打开文件

前提:打开的是一个已经存在的文件

两种open函数:头文件都相同

  int open(const char *pathname, int flags);

运行报错    因为没有a.txt文件 返回-1 显示perror

如果创建a.txt     调用成功就关闭了 

2 open创建新文件

不同用户umask不同,普通用户默认0002   root用户默认0022  也可以自己设置

头文件:

int open(const char *pathname, int flags, mode_t mode);

提问:为什么位或?

答:flags参数是一个int类型的数据,占4个字节,32位, flags 32个位,每一位就是一个标志位。位或是有1即1,位或一个O_CREAT实际上就是将O_CREAT加到之前的O_RDWR上。

3 关闭文件

4 read函数

ssize_t read(int fd, void *buf, size_t count);

5 write函数

 ssize_t write(int fd, const void *buf, size_t count);

例子:将a.txt拷贝到b.txt 

本质:读取a.txt文件的数据写入到b.txt中,利用读写函数

思路:

头文件:

两个文件大小相同,拷贝成功

6 lseek函数

对比:标准C库是通过指针访问,Linux系统函数通过文件描述符访问

off_t lseek(int fd, off_t offset, int whence);

四个作用:

例子:扩展文件的长度

eg:本来11个字节,扩展100 加一个空数据 112个字节

看hello.txt字节数

7 stat函数   查看文件信息

stat   文件名

int stat(const char *pathname, struct stat *statbuf);

1 stat结构体

2 st_mode变量   16位

程序员计算器电脑自带calc

通过a.txt创建软链接b.txt:

通过stat查看b.txt信息:

8 lstat函数   获取 软链接函数信息

int lstat(const char *pathname, struct stat *statbuf);

十 案例:模拟实现  ls  -l 命令   列出当前文件所有信息

列出某个文件所有信息有两种方法:

        1 ls -l  a.txt

        2 模拟实现 ./app a.txt 可以显示文件信息

​​​​​​​

​​​​​​​

  • 18
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值