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;
}