



1.4静态库的制作

首先生成 .o 文件
gcc -c add.c sub.c mult.c div.c

然后
ar rcs libcalc.a add.o sub.o mult.o div.o

1.5 静态库的使用

我们要编译 main.c 文件
gcc main.c -o app -I ./include -l calc -L ./lib
-I 是搜索头文件 -l使用的库 -L搜索库的路径
1.6动态库的制作和使用




编译main.c

运行main报错
它找不到动态库文件
1.7动态库加载失败的原因

当使用动态库中的api时会加载,查找对应的函数,把动态库加载到内存中
使用ldd main 查询动态库依赖关系

1.8解决动态库加载失败的原因
应为需要系统的动态载入器获取动态库的绝对路径,我们把绝对路径配置到搜索的文件中就可以找到了。DT_RPATH改变不了
一、修改环境变量LD_LIBRARY_PATH
(1) (临时的,终端关闭就消失了)

echo $LD_LIRARY_PATH 查看环境变量的值
此时在ldd main

(2) 用户基本的配置

配置 .bashrc 就可以了

使生效

删除配置

(3)系统级别的配置



二、配置 /etc/ld.so.cache文件
不能直接修改这个文件,间接去修改


然后更新

三、将我们的文件放在/lib/ 或 /usr/lib/目录下(不太建议)
因为里面包含了很多系统自带的文件,可能替换,出现错误。
1.9静态库和动态库的对比





一般库比较小用静态库,反而。
1.10


文件很多,我们手动编译很困难


输入:
通过make 执行makefile 里面的规则
1.11

当前目录下查找依赖是否存在。make默认只执行Makefile第一条语句,如果其他语句与第一条无关,则不执行。
修改makefile文件

查找第一条规则中的依赖没有,它会向下去查找其他的规则看能否生成它的依赖。
执行make

如果不改依赖执行 make,不会重新生成目标

修改了main.c 文件,需要重新编译main.c 并生成目标

第二个版本Makefile更好
1.12

修改makefile文件

下面还没有修改

修改后

还可以继续优化


最终修改后

我们在目录中生成了很多不需要的.o文件

然后 执行 make clean
我们生成一个clean文件
![]()
使用make clean不会更新
我们可以把clean设置为伪目标,他就不会生成特定的文件,就不会和外面的文件进行对比,就会执行。

1.13GDB调试



准备工作,加入调试信息到可执行文件中

进入gdb

设置参数

1.14
list 查看当前文件

list 20
list main

用g++编译

gdb中查看别的文件的代码
设置显示的行数

1.15

设置断点

查看断点信息
在其他文件打断点


条件断点

1.16

start + c

run
打断点

run停在第一个断点处

c 继续执行

我们查看bubble.cpp中的bubbleSort函数

next 执行下一行

step 单步执行

c 继续执行回到main.cpp

print i

until

step

循环中的断点出不来
我们删除断点

下面说一下 next 和 step 的区别
可以看出next 遇到调用函数后直接执行完,并不进入函数体中

step会进入调用函数的函数体中一步步执行

finish结束函数体

gdb test 中
打了三个断点

设置自动变量生成a和b 的值

display a display b


重新gdb test


设置i的值

1.17标准c库IO函数和Linux系统IO函数对比

标准c库有缓冲区,Linux没有
标准c库比Linux高级, Linux更底层,Linux适合网络通信,磁盘读写更适合标准c库

1.18虚拟地址空间

1.19文件描述符

1.20open打开文件
虚拟机地址突然变为127.0.0.1,解决办法sudo dhclient -v

open 函数的第一种用法 打开一个存在的文件 man 2 open 查看函数
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 打开一个已经存在的文件
int open(const char *pathname, int flags);
参数:
- pathname:要打开的文件路径
- flags:对文件操作权限设置还有其他的设置
O_RDONLY, O_WRONLY, or O_RDWR 这三个设置时互斥的
返回值:
返回一个新的文件描述符,如果失败返回-1
return the new file descriptor, or -1 if an error occurred(in which case, errno is set appropriately).
errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
#include <stdio.h>
void perror(const char *s);作用:打印erron对应的错误描述
s参数:用户描述,比如hello,最终输出 hello:xxx(实际的错误描述)
// 创建一个新的文件
int open(const char *pathname, int flags, mode_t mode);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
//用来打开一个文件
int fd = open("a.txt",O_RDONLY);
if(fd == -1){
perror("Hello");
}
//关闭
close(fd);
return 0;
}
运行结果

1.21 open创建一个新文件
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 创建一个新的文件
int open(const char *pathname, int flags, mode_t mode);
参数:
- pathname:要创建的文件的路径
- flags:对文件的操作权限和其他的设置
-必选项:O_RDONLY, O_WRONLY, or O_RDWR 这三个设置时互斥的
-可选项:O_CREAT文件不存在,创建新文件
- mode:八进制的数,表示创建出的新的文件的操作权限 比如:0775 最高0777
最终权限为:mode & ~umask
例如:mode:0777 umask:0002 最终为:0775
umask作用是抹去某些权限
二进制 后面9位表示权限每三位一组,前三个当前用户,中间当前用户所在组,后三其他用户
r代表可执行,w可以写,x可执行
flags 参数是一个int类型的数据,占4个字节,32位
flags 32个位,每一位就是一个标志位。
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
//创建一个新的文件
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1){
perror("open");
}
//关闭
close(fd);
return 0;
}

1.22 read、write函数

read 从文件中读取数据到内存中, write 将内存中数据写到文件中
/*
man 2 read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,可以通过他操作某个文件
- buf:需要读取数据存放的地方,数组的地址(传出参数) 从文件中读到内存
- count:指定的数组的大小 数组不一定能装满,
返回值:
- 成功:
>0: 返回实际的读取到的字节数
=0:文件已经读取完了
- 失败: -1 ,并且设置errno
man 2 write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,可以通过他操作某个文件
- buf:要往磁盘写入的数据,数组
- count:要写的数据的实际大小
返回值:
- 成功:返回成功写入的字节大小
- 失败:返回-1, 并设置errno
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
//1.通过open打开english.txt文件
int srcfd = open("english.txt", O_RDONLY);
if(srcfd == -1){
perror("open");
return -1;
}
//2.创建一个新的文件(拷贝文件)
int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if(destfd == -1){
perror("open");
return -1;
}
//3.频繁的读写文件
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0){
write(destfd, buf, len);
}
//4.关闭文件
close(destfd);
close(srcfd);
return 0;
}

1.23 lseek函数
/*
man 3 fseek 标准c库函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
man 2 lseek Linux系统函数
#include <sys/types.h>
#include <unistd.h>
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_CUR);
3. 可以获取文件的长度
lseek(fd, 0, SEEK_END);
4. 拓展文件的长度,当前文件10b,到110b,增加100b
lseek(fd, 100, SEEK_END);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int fd = open("hello.txt", O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
//拓展文件的长度
int ret = lseek(fd, 100, SEEK_END);
if(ret == -1){
perror("lseek");
return -1;
}
//写入空数据才可以拓展长度
write(fd, " ", 1);
//关闭文件
close(fd);
return 0;
}
实现文件拓展


1.24 stat、lstat函数



/*
man 2 stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
作用:获取一个文件相关的一些信息
参数:
- pathname:操作的文件的路径
- statbuff:结构体变量,传出参数,用于保存获取到文件的信息
返回值:
成功:返回0
失败:返回-1,设置errno
int lstat(const char *pathname, struct stat *statbuf);
作用:
参数:
- pathname:操作的文件的路径
- statbuff:结构体变量,传出参数,用于保存获取到文件的信息
返回值:
成功:返回0
失败:返回-1,设置errno
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
struct stat statbuff;
int ret = stat("a.txt", &statbuff);
if(ret == -1){
perror("stat");
return -1;
}
printf("size: %ld\n", statbuff.st_size);
return 0;
}

创建软连接


用stat 打开 获取到的是a.txt的信息,lstat用于获取软连接的信息。
1.25 模拟实现ls -l命令

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
//模拟实现 ls -l指令
//-rw-rw-r-- 1 nowcoder nowcoder 11 9月 29 21:49 a.txt
int main(int argc, char *argv[]){
//判断输入的参数是否正确
if(argc < 2){
printf("%s filename\n", argv[0]);
return -1;
}
//通过stat函数获取用户传入的文件的信息
struct stat st;
int ret = stat(argv[1], &st);
if(ret == -1){
perror("stat");
return -1;
}
//获取文件类型和文件权限
char perms[11] = {0}; //用户保存文件类型和文件权限的字符串
switch (st.st_mode & S_IFMT){
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFREG:
perms[0] = '-';
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFIFO:
perms[0] = 'p';
break;
default:
perms[0] = '?';
}
//判断文件的访问权限
//文件所有者
perms[1] = st.st_mode & S_IRUSR ? 'r' : '-';
perms[2] = st.st_mode & S_IWUSR ? 'w' : '-';
perms[3] = st.st_mode & S_IXUSR ? 'x' : '-';
//文件所在组
perms[4] = st.st_mode & S_IRGRP ? 'r' : '-';
perms[5] = st.st_mode & S_IWGRP ? 'w' : '-';
perms[6] = st.st_mode & S_IXGRP ? 'x' : '-';
//其他用户
perms[7] = st.st_mode & S_IROTH ? 'r' : '-';
perms[8] = st.st_mode & S_IWOTH ? 'w' : '-';
perms[9] = st.st_mode & S_IXOTH ? 'x' : '-';
//硬链接数
int linkNum = st.st_nlink;
//文件所有者
char *fileUser = getpwuid(st.st_uid)->pw_name;
//文件所在组
char *fileGrp = getgrgid(st.st_gid)->gr_name;
//文件的大小
long int fileSize = st.st_size;
//获取修改时间
char *time = ctime(&st.st_mtim);
char mtime[512] = {0};
strncpy(mtime, time, strlen(time) - 1);
char buf[1024];
sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
printf("%s\n", buf);
return 0;
}
有一点bug 在ctime那里传入的参数不一致
1.26 文件属性操作函数

access 判断文件的权限,文件是否存在
chmod 修改文件的权限
chown 修改文件所有者
truncate 缩减或者拓展文件的大小
/*
man 2 access
#include <unistd.h>
int access(const char *pathname, int mode);
作用:判断某个文件是否有某个权限,或者文件是否存在
参数:
- pathname:文件路径
- mode:
R_OK:是否有读权限
W_OK:是否有写权限
X_OK:是否有执行权限
F_OK:文件是否存在
返回值:成功返回0,失败返回-1,并设置erron
*/
#include <stdio.h>
#include <unistd.h>
int main(){
int ret = access("as.txt", F_OK);
if(ret == -1){
perror("access");
return -1;
}
printf("文件存在\n");
return 0;
}

/*
man 2 chmod
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
作用:修改文件的权限
参数:
- pathname:修改文件的路径
- mode:需要修改的权限制,八进制数
返回值:成功返回0,失败返回-1
*/
#include <stdio.h>
#include <sys/stat.h>
int main(){
int ret = chmod("a.txt",0775);
if(ret == -1){
perror("chmod");
return -1;
}
printf("文件权限成功修改\n");
return 0;
}

查看所有者id 和 所在组id
查看组: vim /etc/group
查看id:

/*
man 2 truncate
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
作用:缩减或拓展文件的尺寸到制定的大小
参数:
- pathname:文件的路径
- length:需要最终文件变成的大小
返回值:成功0,失败-1
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int ret = truncate("b.txt", 20);
if(ret == -1){
perror("truncate");
return -1;
}
printf("文件大小已经修改\n");
return 0;
}

1.27 目录操作函数

mkdir 创建一个目录(文件夹)
rmdir 删除一个空目录
rename 对目录重命名
chdir 更改当前的路径(修改当前工作的路径)
getcwd 获取当前的路径
/*
man 2 mkdir
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
作用:创建一个目录
参数:
- pathname:创建目录的路径
- mode:权限,八进制数
返回值:成功0,失败1
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int ret = mkdir("aaa", 0777);
if(ret == -1){
perror("mkdir");
return -1;
}
printf("成功创建目录\n");
return 0;
}

/*
man 2 rename
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
作用:修改目录的名称
参数:
- oldpath:
- newpath:
*/
#include <stdio.h>
int main(){
int ret = rename("aaa", "bbb");
if(ret == -1){
perror("rename");
return -1;
}
printf("成功修改目录名称\n");
return 0;
}

/*
man 2 chdir
#include <unistd.h>
int chdir(const char *path);
作用:修改进程的工作目录
参数:- path 修改后的工作目录
man 2 getcwd
#include <unistd.h>
char *getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
- buf:存储的路径,指向的是一个数组(传出参数)
- size:数组的大小
返回值:
返回的指向的一块内存,这个数据就是第一个参数(buf)
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(){
//获取当前的工作目录
char buf[128];
getcwd(buf, sizeof(buf));
printf("当前的工作目录是:%s\n", buf);
//修改工作目录
int ret = chdir("/home/nowcoder/Linux/lession13");
if(ret == -1){
perror("chdir");
return -1;
}
printf("成功修改工作目录\n");
//创建一个新文件
int fd = open("chdir.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1){
perror("Create");
return -1;
}
printf("成功创建新文件\n");
close(fd);
//获取当前的工作目录
char buf1[128];
getcwd(buf1, sizeof(buf1));
printf("当前的工作目录是:%s\n", buf1);
return 0;
}

1.28 目录遍历函数

opendir 打开目录
readdir 读取目录里内容
closedir 关闭目录
1.29 dup、dup2函数

/*
man 2 dup
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符
fd = 3, int fd1 = dup(),
fd指向的是a.txt,fd1也是指向a.txt
从空闲的文件描述符表中找一个最小的作为新的拷贝的文件描述符
参数:拷贝的文件描述符
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1){
perror("open");
return -1;
}
printf("已经创建了新文件\n");
int fd1 = dup(fd);
if(fd1 == -1){
perror("dup");
return -1;
}
printf("复制了新的文件描述符\n");
printf("fd = %d, fd1 = %d \n", fd, fd1);
close(fd);
char * str = "Hello";
int ret = write(fd1, str, strlen(str));
if(ret == -1){
perror("write");
return -1;
}
printf("通过新的文件描述符写入了数据\n");
return 0;
}

/*
man 2 dup2
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件描述符
oldfd 指向a.txt,newfd 指向b.txt
调用成功后:newfd 和 b.txt 做close,newfd指向了a.txt
oldfd 必须是一个有效的文件描述符
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(){
int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
printf("fd = %d, fd1 = %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
//通过fd1去写数据,实际操作的是1.txt
char *str = "Hello, world";
int len = write(fd1, str, strlen(str));
printf("fd = %d, fd1 = %d, fd2 = %d\n", fd, fd1, fd2);
close(fd);
close(fd1);
return 0;
}

1.30 fcntl函数

/*
man 2 fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... arg );
参数:
- fd:表示需要操作的文件描述符
- cmd:表示对文件描述符进行如何操作
- F_DUPFD 复制文件描述符,
- F_GETFL 获取指定的文件描述符文件状态flags
获取的flags和我们通过open函数传递的flags是一个东西
- F_SETFL 设置文件描述符文件状态flags
必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以修改
可选项:O_APPEND,O_NONBLOCK
O_APPEND 表示追加数据
O_NONBLOCK 设置成非阻塞
阻塞和非阻塞:描述的函数调用的行为。add()
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main(){
//1. 复制文件描述符
//int fd = open("1.txt", O_RDONLY);
//int ret = fcntl(fd, F_DUPFD);
//2. 修改或者获取文件状态flags
int fd = open("1.txt", O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
//获取文件描述符状态flags
int flag = fcntl(fd, F_GETFL);
flag |= O_APPEND;
// 修改文件描述符状态的flags,给flags加入O_APPEND这个标记
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1){
perror("fcntl");
return -1;
}
char *str = "nihao";
int retw = write(fd, str, strlen(str));
if(retw == -1){
perror("write");
return -1;
}
close(fd);
return 0;
}
本文详细介绍了静态库和动态库的制作与使用方法,包括如何生成和利用库文件、解决动态库加载失败的问题,以及静态库与动态库之间的区别。此外还探讨了makefile的应用技巧、GDB调试步骤,并对比了标准C库IO函数与Linux系统IO函数。
1629

被折叠的 条评论
为什么被折叠?



