环境搭建
具体下载安装完整教程后续会补上~
- Ubuntu 18.04 桌面版系统 .iso (sudo apt install gcc g++、 vim)
- VMware 16(创建虚拟机)
- VScode (C/C++拓展包、 JSON文件配置)连接Ubuntu
- Xshell 7 远程连接Ubuntu
- Xftp 7 远程连接Ubuntu
- ssh免密配置
gcc编译过程
- 命令: 以test.c 为例
//用户名@主机名
yang@yang:~/Linux/lession02$ gcc test.c -E -o test.i //-E 生成预处理后的文件test.i
yang@yang:~/Linux/lession02$ gcc test.i -S -o test.s//-S 汇编文件.s
yang@yang:~/Linux/lession02$ gcc test.s -s -o test.o//-s 生成可执行程序
//(注 .out 是linux系统中可执行文件类似 window下的.exe)
心态崩了 这csdn不会自动保存吗?!!!!(之前写的内容意外缺失:gcc、 静态库制作与使用 、动态库制作与使用、两者对比)
在这里插入图片描述
- gcc编译选项汇总
静态库
制作
使用
-I
指明当前.c文件头文件的位置-L
指明当前静态库的位置-l
指明当前静态库(libxxx)的名字xxx
动态库
制作与使用
- 生产.c 的运行文件
- 运行main ,出现报错 下节解决。
动态库生成后,运行可执行文件报错解决
静态库下,gcc链接时,静态库文件打包到可执行文件里面
动态库下,gcc链接时,动态库文件并没有打包到可执行文件里面,只有动态库的名字
查询 main(可执行文件)的依赖关系,可通过命令 ldd
来查看。下图可以发现libcalc.so的路径配置并不存在。
解决方式
- LD_LIBRARY_PATH 环境变量配置
- 暂时性配置(关闭连接后重新启动配置重置)直接在命令行中 输入
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:(/动态库路径/)
- 用户级配置
在最顶层目录下的bashrc文件中最后一行添加下面这个。 然后:wq
写入后保存退出到命令行
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:(/动态库路径/)
注上述动态库的完整路径 可在库所在文件夹下通过命令 pwd
来查询
- 系统级(永久)
用sudo 打开profile文件最后一行输入 export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:(/动态库路径/)
- 第二种方法
在文件中直接添加(/动态库路径/)
静态库与动态库优缺点及建议
当库较小时一般用静态库,反之用动态库
- 动态库图解
Makefile编写
首先vim Makefile
输入下图内容保存退出 ,输入make
运行
Makefile快捷编写
编写前
用预定义变量编写后
方便了很多!
GDB调试
放入调试信息到执行文件中,会比一般相同的可执行文件大
gcc test.c -o test -g
输入 gdb test
进入GDB调试页面(注意test 是可执行文件)
list 相关命令
断点操作命令
调试命令
文件IO
相对于内存来说的IO,输入就是内存从文件中读取,输出就是内存将数据输出到文件中
虚拟内存概念
某个文件存储的实际物理内存不是连续的,为了方便起见,使用虚拟内存,由CPU中的逻辑单元将虚拟内存映射到实际物理内存。
文件描述符
- 文件描述符表:前三个默认被占用,每打开一个文件拥有一个描述符。一个文件可以打开多次,故有多个对应的描述符。
- 每个进程默认分配1024给文件描述符表,所以一个进程可以打开1024个文件。
open打开文件
- open函数
int open(const char* pathname,int flag)//用于打开文件
参数:
- pathname :打开文件路径
- flag : 有三种分别是只读、只写、可读可写。 O_RONLY 、O_WONLY、O_RDWR 具有互斥性
- 返回值 :返回一个新的文件描述符,发生错误时返回 -1
int open(const char* pathname, int flag,mode_t mode)//用于创建文件
- errno是属于Linux的函数库,是库里面的全局变量,用来记录最近发生错误的错误号。
下列代码属于 linux 里面文件下的, 运用命令man 3 perror
查看
#include <stdio.h>
void perror(const char *s); //作用:打印errno对应的错误描述
#include <errno.h>
- 整体代码如下 (文件打开关闭,错误描述打印)
#include <sys/types.h> //调用open函数 需要用到的库 Linux 命令:man 2 open 即可查询到
#include <sys/stat.h> //调用open函数 需要用到的库 Linux 命令:man 2 open
#include <fcntl.h> //调用open函数 需要用到的库 Linux 命令:man 2 open
#include <stdio.h> //perror 在该库下 Linux 命令:man 3 perror
#include <unistd.h> //close在该库下 Linux 命令:man 2 close
int main() {
// 打开一个文件
int fd = open("a.txt", O_RDONLY);
if(fd == -1) {
perror("open"); //输出为 open:xxx。 xxx为错误描述
}
// 读写操作
// 关闭
close(fd);
return 0;
}
- 输出结果
perror("open"); //输出为 open:xxx。 xxx为错误描述
copy文件内容
注意读写过程。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
//打开源文件 检测是否成功打开
int srcfd = open("english.txt",O_RDONLY );
if(srcfd == -1) {
perror("open1");
return -1;
}
//创建目标文件 检测是否成功打开
int desfd = open("copyI.txt",O_WRONLY | O_CREAT,0664);
if(srcfd == -1) {
perror("open2");
return -1;
}
//读写操作 利用buf
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd,buf,sizeof(buf))) > 0) { //注意括号
write(desfd,buf,len);
}
//关闭文件,一定要记得
close(srcfd);
close(desfd);
return 0;
}
lseek 函数 (man 3 lseek)
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd: 文件描述符
- -offset: 偏移量,
- whence:
SEEK_SET
设置文件指针的偏移量
SEEK_CUR
设置偏移量:当前位置 + 第二个参数offset的值
SEEK_END
设置偏移量:文件大小 + 第二个参数offset的值
函数作用:
- 移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
- 获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
- 获取文件长度
lseek(fd, 0, SEEK_END);
- 拓展文件的长度,当前文件10b, 110b, 增加了100个字节
lseek(fd, 100, SEEK_END)
注意:需要写一次数据
stat、lstat 函数
int stat(const char *pathname, struct stat *statbuf);
//获取文件相关信息 保存到结构体中
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main() {
struct stat statbuf;
int ret = stat("a.txt", &statbuf);
if(ret == -1) {
perror("stat");
return -1;
}
printf("size: %ld\n", statbuf.st_size); //输入文件大小的信息
return 0;
}
-
在Linux下输入
stat filename
也可直接查看信息
-
int lstat(const char *pathname, struct stat *statbuf);
//获取软连接文件相关信息 保存到结构体中(而且该文件是软链接文件,即该文件指向某源文件)
-
关于结构体stat的变量
-
关于stat结构体中 st_mode
S_IFSOCK 那些都是八进制
模拟 ls -l
lesson10 /ls-l.c
//模拟实现ls -l 指令
//两个参数 :一个为 argv[0] = "ls" 另一个argv[1] ="filename"
//-rw-rw-r-- 1 yang yang 1067 9月 28 18:03 stat.c
int main (int argc,char * argv[]) {
//传入参数个数少于两个 输入错了
if(argc < 2) {
//给出提示
printf("%s filename\n" , argv[0]);
return -1;
}
//将传入的文件名进行处理
struct stat statfile;
int ret = stat(argv[1],&statfile);
if(ret == -1) {
perror("stat");
return -1;
}
..
.
.
.
.
文件属性操作函数
#include <unistd.h>
int access(const char *pathname, int mode);
- 作用:判断某个文件是否有某个权限,或者判断文件是否存在
- 参数:
- pathname: 判断的文件路径
- mode:
R_OK: 判断是否有读权限
W_OK: 判断是否有写权限
X_OK: 判断是否有执行权限
F_OK: 判断文件是否存在
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
- 作用:修改文件的权限
- 参数:
pathname: 需要修改的文件的路径
mode:需要修改的权限值,八进制的数
返回值:成功返回0,失败返回-1
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
- 作用:缩减或者扩展文件的尺寸至指定的大小
- 参数:
path: 需要修改的文件的路径
length: 需要最终文件变成的大小
返回值:成功返回0, 失败返回-1
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
- 作用:更改由路径名指定的文件的所有权,如果它是符号链接,则取消引用。
- Linux下
etc/passwd
存放了所有用户的信息和用户组的信息
操作目录函数
其中 char* getcwd(char * buf,size_t size);
是获取当前工作目录
目录遍历函数
- dirent 结构体
DIR * dir = opendir(Path);
dir->xxx 即可输出 对应结构体内变量
文件描述符 操作函数
- 一个文件可以由多个文件描述符指向。
- dup函数 创建一个 与oldfd 相同的文件描述符,且其在文件描述符表中的处于空闲处的最小处。
如 oldfd = 3 那么 使用int newfd = sup(oldfd)
后 newfd = 4 - duq2 函数 使用
int fd = duq2(fd1,fd2)
后这个函数会先判断fd1和fd2是不是同一个值,如果是的就直接返回fd2。如果不是的,它会先把fd2指向的文件关闭,然后把fd1复制给fd2然后把fd2返回。
- 文件描述符表
fcntl函数
功能很多(列举部分)
/*
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
参数:
fd : 表示需要操作的文件描述符
cmd: 表示对文件描述符进行如何操作
- F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd, F_DUPFD);
- F_GETFL : 获取指定的文件描述符文件状态flag
获取的flag和我们通过open函数传递的flag是一个东西。
- F_SETFL : 设置文件描述符文件状态flag
必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
可选性:O_APPEND, O)NONBLOCK
O_APPEND 表示追加数据 // 不进行O_APPEND 会覆盖原先数据
NONBLOK 设置成非阻塞
阻塞和非阻塞:描述的是函数调用的行为。
*/
O_APPEND 表示追加数据 ,不进行 flag |= O_APPEND; // flag = flag | O_APPEND
会从头开始覆盖原先数据!,反之,会从尾部开始添加。
// 获取文件描述符状态flag
int flag = fcntl(fd, F_GETFL);
if(flag == -1) {
perror("fcntl");
return -1;
}
flag |= O_APPEND; // flag = flag | O_APPEND
// 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1) {
perror("fcntl");
return -1;
}