gcc 获得 .o 文件 将 .o 文件用 ar 工具打包
ar rcs libxxx.a xxx.o xxx.o
# -r 将文件插入备份文件中
# -c 建立备份文件
# -s 索引
g++ main.cpp -I ./include -l calc -L./lib -o test && ./test
# -I 包含路径
# -l 库名字
# -L 库路径
gcc -c -fpic *.c # 得到和位置无关的代码
gcc -shared *.o -o libxxx.so # 生成动态库
g++ main.cpp -I ./include -L lib/ -l calc -o main && ./main
# ./main: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
程序启动之后,动态库会被动态加载到内存,通过 ldd (list dynamic dependencies) 命令检查动态库依赖关系
ldd main
# linux-vdso.so.1 (0x00007ffd771f6000)
# libcalc.so => not found
# libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f47d0be9000)
# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f47d09f7000)
# libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f47d08a8000)
# /lib64/ld-linux-x86-64.so.2 (0x00007f47d0e28000)
# libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f47d088d000)
如何定位共享库文件呢?
当系统加载可执行代码时,能够知道其所依赖的库的名字,但还是需要知道绝对路径。
# 方法一, 临时用
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/bin
./main
ldd main# 方法二, 用户级别
# 写到 .shrc 中# 方法三, 系统级别
# 写到 /etc/profile
sudo nano /etc/ld.so.conf
sudo ldconfig
优点:
缺点
优点:
缺点
makefile 或者 Makefile
一个 Makefile 文件中可以有一个或多个规则
目标: 最终要生成的文件(伪目标除外)
依赖: 生成目标所需要的文件或是目标
命令: 通过执行命令对依赖操作生成目标(命令前必须 Tab 缩进)
Makefile 中的其他规则一般都是为第一条规则服务的
命令在执行之前, 需要先检查规则中的依赖是否存在
检测更新,在执行规则中的命令是,会比较目标文件和依赖文件的时间
自定义变量: 变量名=变量值 var=hello
预定义变量:
获取变量的值: $(变量名)
$(wildcard PATTERN)
$(patsubst ,,
g++ main.cpp -g -Wall -o test
gdb test
查看当前文件代码
查看非当前文件代码
设置显示的行数
回车 : 上一次命令
设置断点
查看断点
删除断点
设置断点无效
设置断点生效
设置断点条件
运行gdb 程序
继续运行, 到下一个断点才停
向下执行一行代码,不会进入函数体
变量操作
向下单步调试,遇到函数进入函数体
自动变量操作
其他操作
fopen, fclose, fread, fwrite, fgets, fputs, fscanf, fprintf, fseek, fgetc, fputc, ftell, feof, fflush…
结构体: 文件描述符(整型值), 文件读写指针位置,IO缓冲区(内存地址)
用户程序 <–> C标准I/O库 <–> 内核(Read/Write) <–> 磁盘
Linux 下的可执行文件格式: ELF
| 用户区 | 内核区 |
|---|---|
| 0-3G | 3G-4G |
从左到右:
用户区
内核区 内核空间是受保护的,用户不能对该空间进行读写操作,否则会出现段错误。
PCB 进程控制块
文件描述符
#include
#include
#include
#include
#include int main(int argc, char **argv) {// 打开一个已经存在的文件// int open(const char* pathname, int flags);// int open(const char* pathname, int flags, mode_t mode );// 参数: pathname: 要创建的文件路径// flags: 对文件的操作权限和其他设置// mode: 八进制数,表示用户对新创建的文件的操作权限, 最终权限是 (mode & ~umask)// 返回一个新的文件描述符, 如果失败,返回 -1。// errno: 属于Linux 系统函数库, 库里面的一个全局变量,记录的是最近的错误号。/**#includevoid perror(const char *s);作用: 打印 errno 对应的错误描述s 参数: 用户描述,比如 hello, 最终输出的内容的是 hello: xxx(实际的错误输出)*/int fd = open("./a.txt", O_RDONLY);if (-1 == fd) {perror("open");}close(fd);return 0;
}
#include
#include
#include
#include
#include int main() {// 创建一个新的文件int fd = open("create.txt", O_RDWR |O_CREAT, 0775);if(-1 == fd) {perror("open create err");}close(fd);return 0;}
#include
#include
#include
#include
#include int main() {/**#include ssize_t read(int fd, void *buf, size_t count);参数: fd: 文件描述符, open得到,通过这个文件描述符操作某个文件buf: 需要读取数据存放的地方,数组的地址count: 指定数组的大小返回值: 成功, 大于0, 返回实际读取到的字节数, 0 文件读取完,失败-1 文件读取失败#include ssize_t write(int fd, const void *buf, size_t count);*/// 1. open 打开文件int fd = open("read_write.c", O_RDONLY);if(-1 == fd){perror("open err");return -1;}// 2. 创建一个新的文件(copy 文件)int dest_fd = open("cpy.txt", O_WRONLY| O_CREAT, 0777);if(-1 == dest_fd){perror("create err");return -1;}// 3. 频繁的读写操作char buffer[1024] = {0};int len = 0;while((len = read(fd, buffer, sizeof(buffer))) > 0){write(dest_fd, buffer, len);}// 4. 关闭文件close(dest_fd);close(fd);return 0;
}
/**// 标准C库函数#include int fseek(FILE *stream, long offset, int whence);// Linux 系统函数库#include #include off_t lseek(int fd, off_t offset, int whence);参数: fd: 通过 open 得到,文件描述符offset: 偏移量whence: SEEK_SET 设置文件指针的偏移量SEEK_CUR 设置偏移量,当前位置 + 第二个参数的 offset 的值SEEK_END 设置偏移量,文件大小 + 第二个参数的 offset 的值返回值: 返回文件指针的位置作用: 1. 移动文件指针到文件头lseek(fd, 0, SEEK_SET);2. 获取当前文件指针的位置lseek(fd, 0,SEEK_SET);3. 获取文件长度lseek(fd, 0, SEEK_END);4. 扩展文件的长度, 当前文件 10b, 110b, 增加了100个字节lseek(fd, 100, SEEK_END)
*/#include
#include
#include
#include
#include int main() {int fd = open("cpy.txt", O_RDWR);if (-1 == fd) {perror("read err");return -1;}// 扩展文件的长度int ret = lseek(fd, 100, SEEK_END);if (-1 == ret) {perror("lseek");return -1;}write(fd , " ", 1);close(fd);return 0;
}
注: 需要写一次数据才能改变大小
/**#include #include #include int stat(const char *pathname, struct stat *statbuf);作用:获取一个文件相关的一些信息参数:- pathname: 操作文件的路径- statbuf: 结构体变量,传出参数,用于保存获取到的文件信息。返回值:- 成功: 返回0,- 失败: 返回-1, 设置 errnoint lstat(const char *pathname, struct stat *statbuf);作用: 获取软连接文件的信息
*/#include
#include
#include
#include int main() {struct stat statbuf;// int ret = stat("cpy_s.txt", &statbuf);int ret = lstat("cpy_s.txt", &statbuf);if (-1 == ret) {perror("stat err");return -1;}printf("size: %1d\n", (int)statbuf.st_size);return 0;
}
#include
#include
#include
#include
#include
#include
#include /***/
int main(int argc, char *argv[]) {// 判断输入的参数是否正确if (argc < 2) {printf("%s filename\n", argv[0]);return -1;}// 通过 stat 过去文件的信息struct stat buffstat;int ret = stat(argv[1], &buffstat);if (-1 == ret) {perror("stat err");return -1;}// 获取文件类型和文件权限char permit[11] = {0};switch (buffstat.st_mode & S_IFMT) {case S_IFLNK:permit[0] = '1';break;case S_IFDIR:permit[0] = 'd';break;case S_IFREG:permit[0] = '-';break;case S_IFBLK:permit[0] = 'b';break;case S_IFSOCK:permit[0] = 's';break;case S_IFCHR:permit[0] = 'c';break;case S_IFIFO:permit[0] = 'p';break;default:permit[0] = '?';break;}// 判断文件的访问权限permit[1] = (buffstat.st_mode & S_IRUSR) ? 'r' : '-';permit[2] = (buffstat.st_mode & S_IWUSR) ? 'w' : '-';permit[3] = (buffstat.st_mode & S_IXUSR) ? 'x' : '-';permit[4] = (buffstat.st_mode & S_IRGRP) ? 'r' : '-';permit[5] = (buffstat.st_mode & S_IWGRP) ? 'w' : '-';permit[6] = (buffstat.st_mode & S_IXGRP) ? 'x' : '-';permit[7] = (buffstat.st_mode & S_IROTH) ? 'r' : '-';permit[8] = (buffstat.st_mode & S_IWOTH) ? 'w' : '-';permit[9] = (buffstat.st_mode & S_IXOTH) ? 'x' : '-';// 硬连接数int linkNum = buffstat.st_nlink;char *fileUser = getpwuid(buffstat.st_uid)->pw_name;char *fileGroup = getpwuid(buffstat.st_gid)->pw_name;// 文件大小long int fileSize = buffstat.st_size;// 获取修改时间char *changeTime = ctime(&buffstat.st_mtime);char mtime[512] = {0};strncpy(mtime, changeTime, strlen(changeTime) - 1);char buf[1024];sprintf(buf, "%s %d %s %s %ld %s %s", permit, linkNum, fileUser, fileGroup,fileSize, mtime, argv[1]);printf("%s\n", buf);return 0;
}
/**#include int access(const char *pathname, int mode);作用: 判断某个文件是否有某个权限, 或者判断文件是否存在参数: pathname: 判断文件路径mode: R_OK, W_OK, X_OK: 判断是否有相应权限返回值: 0 成功,-1 失败
*/#include
#include int main() {int ret = access("cpy.txt", R_OK);if (-1 == ret) {perror("access err");return -1;}printf("file exist!!! \n");return 0;
}
/**#include int chmod(const char *pathname, mode_t mode);作用: 修改文件的权限参数: pathname: 要修改文件的路径mode: 需要修改的权限值,八进制的数返回值:成功 0, 失败 -1
*/
#include
#include int main() {int ret = chmod("cpy.txt", 0775);if (-1 == ret) {perror("chmod err");return -1;}return 0;
}
/**#include int chown(const char *pathname, uid_t owner, gid_t group);
*/
/**#include #include int truncate(const char *path, off_t length);作用: 缩减或者扩展文件的尺寸到指定大小参数: path: 需要修改的文件的路径length: 需要最终文件变成的大小返回值: 成功 0, 失败 -1.
*/#include
#include
#include int main() {int ret = truncate("cpy.txt", 20);if (-1 == ret) {perror("truncate err");return -1;}return 0;
}
/**// mkdir#include#include int mkdir(const char *pathname, mode_t mode);参数: mode: 八进制的数// rmdir#include int rmdir(const char *pathname);作用: 删除空目录// rename#include int rename(const char *oldpath, const char *newpath);// chdir#include int chdir(const char *path);作用:修改进程的工作目录// getcwd#include char *getcwd(char *buf, size_t size);作用: 获取当前的工作目录参数: buf:存储的路径,指向的是一个数组(传出参数)size: 数组大小返回值: 返回的指向的一块内存, 这个数据就是第一个参数
*/#include
#include
#include
#include
#include
#include int main(){// 获取当前的工作目录char buff[128];getcwd(buff, sizeof(buff));printf("current work path:%s \n", buff);// 修改工作目录int ret = chdir("../");int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);if(-1 == fd){perror("open err");return -1;}close(fd);// 获取当前的工作目录char buffCur[128];getcwd(buffCur, sizeof(buffCur));printf("now current work path: %s\n", buffCur);return 0;
}
/**#include #include DIR *opendir(const char *name);参数:name : 需要打开的目录的名称返回值: DIR* 类型,理解为名录流,错误返回 NULL;#include struct dirent *readdir(DIR *dirp);作用: 读取目录中的数据参数: dirp 通过 opendir 返回的结果返回值:struct dirent :代表读取到的文件信息,读取到末尾或失败,返回NULL;#include #include int closedir(DIR *dirp);作用: 关闭目录
*/#include "string.h"
#include
#include
#include
#include // 获取目录下所有普通文件的个数
int getFIleNumber(const char *path) {DIR *dir = opendir(path);if (dir == NULL) {perror("opendir");exit(0);}struct dirent *prt;int total_number = 0;while ((prt = readdir(dir))) {// 获取名称char *dname = prt->d_name;// 忽略 ./ 和 ../ 两个目录if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {continue;}// 判断是否是普通文件还是目录if (prt->d_type == DT_DIR) {// 目录, 需要继续读取这个目录char newPath[256];sprintf(newPath, "%s/%s", path, dname);total_number += getFIleNumber(newPath);}if (prt->d_type == DT_REG) {/// 普通文件++total_number;}}// 关闭目录closedir(dir);return total_number;
}// 读取某个目录下文件的个数
int main(int argc, char *argv[]) {if (argc < 2) {printf("%s path\n", argv[0]);return -1;}int file_numbers = getFIleNumber(argv[1]);printf("file numbers: %d\n", file_numbers);return 0;
}
/**#include int dup(int oldfd);作用: 复制一个新的文件描述符,和原来的文件描述符指向同一个文件,从空闲文件描述符表中找一个最小的,作为新的拷贝的文件描述符int dup2(int oldfd, int newfd);
*/#include
#include
#include
#include
#include
#include int main() {int fd1 = open("a.txt", O_RDWR | O_CREAT, 0664);int fd2 = dup(fd1);if (fd2 == -1) {perror("dup");return -1;}printf("fd1: %d, fd2: %d\n", fd1, fd2);close(fd1);char *str = "helloworld";int ret = write(fd2, str, strlen(str));if (-1 == ret) {perror("write");return -1;}close(fd2);return 0;
}
/**#include int dup(int oldfd);作用: 复制一个新的文件描述符,和原来的文件描述符指向同一个文件,从空闲文件描述符表中找一个最小的,作为新的拷贝的文件描述符int dup2(int oldfd, int newfd);作用: 重定向文件描述符,oldfd 指向 a.txt, newfd 指向 b.txt, 调用函数成功后, newfd 和 b.txtclose, newfd 指向了a.txt。 oldfd 必须是一个有效的文件描述符, oldfd 和 newfd相同,相当于什么都没做
*/#include
#include
#include
#include
#include
#include int main() {//int fd = open("1.txt", O_RDWR | O_CREAT, 0664);if (fd == -1) {perror("open");return -1;}int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);if (-1 == fd1) {perror("open");return -1;}printf("fd: %d, fd1 %d \n", fd, fd1);int fd2 = dup2(fd, fd1);if (fd2 == -1) {perror("dup2");return -1;}// 通过 fd1 去写数据, 实际操作的是 1.txt, 而不是 2.txtchar str[] = "hello dup2";int len = write(fd1, str, strlen(str));if (len == -1) {perror("write");return -1;}printf("fd: %d, fd1: %d, fd2: %d\n", fd, fd1, fd2);close(fd1);// close(fd2);close(fd);return 0;
}
/**#include #include int fcntl(int fd, int cmd, ... args);参数: fd 需要操作的文件描述符cmd : 表示对文件描述符进行如何操作1. 复制文件描述符, F_DUPFD2. F_GETFL 获取指定文件描述符状态 flag, 获取的flag 和通过 open传递的flag 是一个东西。3. F_SETFL 设置文件描述符状态 flag, 必选项 O_RDONLY, O_WRONLY, O_RDWR,可选项: O_APPEND, NONBLOCK, O_APPEND 表示追加数据, NONBLOK 设置能非阻塞。阻塞和非阻塞: 描述的是函数调用的行为。
*/#include
#include
#include
#include
#include
#include int main() {// 1. 复制文件描述符/* int fd = open("1.txt", O_RDONLY); *//* int ret = fcntl(fd , F_DUPFD); */// 2. 修改或者获取文件状态 flagint fd = open("1.txt", O_RDWR);if (-1 == fd) {perror("open");return -1;}// 获取文件描述符的状态int flag = fcntl(fd, F_GETFL);if (-1 == flag) {perror("fcntl");return -1;}flag |= O_APPEND;// 修改文件描述符状态的 flag, 给 flag 加入 O_APPEND 这个标记int ret = fcntl(fd, F_SETFL, flag);if (-1 == ret) {perror("fcntl");return -1;}char str[] = "\nhello world from fcntl\n";write(fd, str, strlen(str));close(fd);return 0;
}