服务器项目课程学习20220221
Linux系统编程入门(一)
GCC
什么是GCC
编程语言的发展
计算机<--(运行)---机器语言<---(汇编)-----汇编语言<----(编译)----高级语言
GCC工程流程
源代码---(预处理器)----->预处理后源代码(.i)---->编译器----->汇编代码------>汇编器---->目标代码/启动代码/库代码/其他目标代码------->链接器------>可执行文件(.exe/.out)
.h
.c
.cpp
gcc和g++的区别
都是GNU(组织)的一个编译器。
GCC常用参数
静态库的制作和使用
什么是库
静态库的制作
makefile
什么是makefile
makefile文件命名和规则
makefile工作原理
变量
makefile和GDB调试
makefile
变量
模式匹配
函数
GDB调试
什么是GDB调试
准备工作
GDB命令-启动、退出、查看代码
GDB命令-断点操作
GDB命令-调试命令
标准C库IO函数和Linux系统IO函数的对比
IO函数是站在内存的角度输入输出
标准C库IO函数(第三方库IO函数)
标准C库IO和Linux系统IO的关系
虚拟地址空间
虚拟地址空间
虚拟地址空间是不存在的,是想象出来的,是用来干啥的呢?
程序和进程的区别:程序只是在磁盘上的代码。运行中的代码加载到内存中,是进程,进程也就是运行中的程序。
MMU:内存管理单元
文件描述符
文件描述符
open打开文件
open打开文件的代码框架
open创建新文件
man 2是linux系统的内容,man 3是标准库里面的内容。
open函数的使用
read、write函数
代码:
1 /*
2 #include <unistd.h>
3 ssize_t read(int fd, void *buf, size_t count);
4 参数:
5 - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
6 - buf:需要读取数据存放的地方,数组的地址(传出参数)
7 - count:指定的数组的大小
8 返回值:
9 - 成功:
10 >0: 返回实际的读取到的字节数
11 =0:文件已经读取完了
12 - 失败:-1 ,并且设置errno
13
14 #include <unistd.h>
15 ssize_t write(int fd, const void *buf, size_t count);
16 参数:
17 - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
18 - buf:要往磁盘写入的数据,数据
19 - count:要写的数据的实际的大小
20 返回值:
21 成功:实际写入的字节数
22 失败:返回-1,并设置errno
23 */
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29
30 int main() {
31
32 // 1.通过open打开english.txt文件
33 int srcfd = open("english.txt", O_RDONLY);
34 if(srcfd == -1) {
35 perror("open");
36 return -1;
37 }
38
39 // 2.创建一个新的文件(拷贝文件)
40 int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
41 if(destfd == -1) {
42 perror("open");
43 return -1;
44 }
45
46 // 3.频繁的读写操作
47 char buf[1024] = {0};
48 int len = 0;
49 while((len = read(srcfd, buf, sizeof(buf))) > 0) {
50 write(destfd, buf, len);
51 }
52
53 // 4.关闭文件
54 close(destfd);
55 close(srcfd);
56
57
58 return 0;
59 }
lseek函数
代码:
1 /*
2 标准C库的函数
3 #include <stdio.h>
4 int fseek(FILE *stream, long offset, int whence);
5
6 Linux系统函数
7 #include <sys/types.h>
8 #include <unistd.h>
9 off_t lseek(int fd, off_t offset, int whence);
10 参数:
11 - fd:文件描述符,通过open得到的,通过这个fd操作某个文件
12 - offset:偏移量
13 - whence:
14 SEEK_SET
15 设置文件指针的偏移量
16 SEEK_CUR
17 设置偏移量:当前位置 + 第二个参数offset的值
18 SEEK_END
19 设置偏移量:文件大小 + 第二个参数offset的值
20 返回值:返回文件指针的位置
21
22
23 作用:
24 1.移动文件指针到文件头
25 lseek(fd, 0, SEEK_SET);
26
27 2.获取当前文件指针的位置
28 lseek(fd, 0, SEEK_CUR);
29
30 3.获取文件长度
31 lseek(fd, 0, SEEK_END);
32
33 4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
34 lseek(fd, 100, SEEK_END)
35 注意:需要写一次数据
36
37 */
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <stdio.h>
44
45 int main() {
46
47 int fd = open("hello.txt", O_RDWR);
48
49 if(fd == -1) {
50 perror("open");
51 return -1;
52 }
53
54 // 扩展文件的长度
55 int ret = lseek(fd, 100, SEEK_END);
56 if(ret == -1) {
57 perror("lseek");
58 return -1;
59 }
60
61 // 写入一个空数据
62 write(fd, " ", 1);
63
64 // 关闭文件
65 close(fd);
66
67 return 0;
68 }
stat、lstat函数
stat结构体
代码:
1 /*
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <unistd.h>
5
6 int stat(const char *pathname, struct stat *statbuf);
7 作用:获取一个文件相关的一些信息
8 参数:
9 - pathname:操作的文件的路径
10 - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
11 返回值:
12 成功:返回0
13 失败:返回-1 设置errno
14
15 int lstat(const char *pathname, struct stat *statbuf);
16 参数:
17 - pathname:操作的文件的路径
18 - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
19 返回值:
20 成功:返回0
21 失败:返回-1 设置errno
22
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <stdio.h>
29
30 int main() {
31
32 struct stat statbuf;
33
34 int ret = stat("a.txt", &statbuf);
35
36 if(ret == -1) {
37 perror("stat");
38 return -1;
39 }
40
41 printf("size: %ld\n", statbuf.st_size);
42
43
44 return 0;
45 }
模拟实现ls -l命令
第一个是模拟的, 第二个是直接的不是模拟的。
代码:
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <unistd.h>
5 #include <pwd.h>
6 #include <grp.h>
7 #include <time.h>
8 #include <string.h>
9
10 // 模拟实现 ls -l 指令
11 // -rw-rw-r-- 1 nowcoder nowcoder 12 12月 3 15:48 a.txt
12 int main(int argc, char * argv[]) {
13
14 // 判断输入的参数是否正确
15 if(argc < 2) {
16 printf("%s filename\n", argv[0]);
17 return -1;
18 }
19
20 // 通过stat函数获取用户传入的文件的信息
21 struct stat st;
22 int ret = stat(argv[1], &st);
23 if(ret == -1) {
24 perror("stat");
25 return -1;
26 }
27
28 // 获取文件类型和文件权限
29 char perms[11] = {0}; // 用于保存文件类型和文件权限的字符串
30
31 switch(st.st_mode & S_IFMT) {
32 case S_IFLNK:
33 perms[0] = 'l';
34 break;
35 case S_IFDIR:
36 perms[0] = 'd';
37 break;
38 case S_IFREG:
39 perms[0] = '-';
40 break;
41 case S_IFBLK:
42 perms[0] = 'b';
43 break;
44 case S_IFCHR:
45 perms[0] = 'c';
46 break;
47 case S_IFSOCK:
48 perms[0] = 's';
49 break;
50 case S_IFIFO:
51 perms[0] = 'p';
52 break;
53 default:
54 perms[0] = '?';
55 break;
56 }
57
58 // 判断文件的访问权限
59
60 // 文件所有者
61 perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
62 perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
63 perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
64
65 // 文件所在组
66 perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
67 perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
68 perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
69
70 // 其他人
71 perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
72 perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
73 perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
74
75 // 硬连接数
76 int linkNum = st.st_nlink;
77
78 // 文件所有者
79 char * fileUser = getpwuid(st.st_uid)->pw_name;
80
81 // 文件所在组
82 char * fileGrp = getgrgid(st.st_gid)->gr_name;
83
84 // 文件大小
85 long int fileSize = st.st_size;
86
87 // 获取修改的时间
88 char * time = ctime(&st.st_mtime);
89
90 char mtime[512] = {0};
91 strncpy(mtime, time, strlen(time) - 1);
92
93 char buf[1024];
94 sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
95
96 printf("%s\n", buf);
97
98 return 0;
99 }
文件属性操作函数
代码:
1 /*
2 #include <sys/stat.h>
3 int chmod(const char *pathname, mode_t mode);
4 修改文件的权限
5 参数:
6 - pathname: 需要修改的文件的路径
7 - mode:需要修改的权限值,八进制的数
8 返回值:成功返回0,失败返回-1
9
10 */
11 #include <sys/stat.h>
12 #include <stdio.h>
13 int main() {
14
15 int ret = chmod("a.txt", 0777);
16
17 if(ret == -1) {
18 perror("chmod");
19 return -1;
20 }
21
22 return 0;
23 }
1 /*
2 #include <unistd.h>
3 int access(const char *pathname, int mode);
4 作用:判断某个文件是否有某个权限,或者判断文件是否存在
5 参数:
6 - pathname: 判断的文件路径
7 - mode:
8 R_OK: 判断是否有读权限
9 W_OK: 判断是否有写权限
10 X_OK: 判断是否有执行权限
11 F_OK: 判断文件是否存在
12 返回值:成功返回0, 失败返回-1
13 */
14
15 #include <unistd.h>
16 #include <stdio.h>
17
18 int main() {
19
20 int ret = access("a.txt", F_OK);
21 if(ret == -1) {
22 perror("access");
23 }
24
25 printf("文件存在!!!\n");
26
27 return 0;
28 }
1 /*
2 #include <unistd.h>
3 #include <sys/types.h>
4 int truncate(const char *path, off_t length);
5 作用:缩减或者扩展文件的尺寸至指定的大小
6 参数:
7 - path: 需要修改的文件的路径
8 - length: 需要最终文件变成的大小
9 返回值:
10 成功返回0, 失败返回-1
11 */
12
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <stdio.h>
16
17 int main() {
18
19 int ret = truncate("b.txt", 5);
20
21 if(ret == -1) {
22 perror("truncate");
23 return -1;
24 }
25
26 return 0;
27 }
目录操作函数
代码:
1 /*
2 #include <sys/stat.h>
3 #include <sys/types.h>
4 int mkdir(const char *pathname, mode_t mode);
5 作用:创建一个目录
6 参数:
7 pathname: 创建的目录的路径
8 mode: 权限,八进制的数
9 返回值:
10 成功返回0, 失败返回-1
11 */
12
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <stdio.h>
16
17 int main() {
18
19 int ret = mkdir("aaa", 0777);
20
21 if(ret == -1) {
22 perror("mkdir");
23 return -1;
24 }
25
26 return 0;
27 }
1 /*
2 #include <stdio.h>
3 int rename(const char *oldpath, const char *newpath);
4
5 */
6 #include <stdio.h>
7
8 int main() {
9
10 int ret = rename("aaa", "bbb");
11
12 if(ret == -1) {
13 perror("rename");
14 return -1;
15 }
16
17 return 0;
18 }
1 /*
2
3 #include <unistd.h>
4 int chdir(const char *path);
5 作用:修改进程的工作目录
6 比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
7 参数:
8 path : 需要修改的工作目录
9
10 #include <unistd.h>
11 char *getcwd(char *buf, size_t size);
12 作用:获取当前工作目录
13 参数:
14 - buf : 存储的路径,指向的是一个数组(传出参数)
15 - size: 数组的大小
16 返回值:
17 返回的指向的一块内存,这个数据就是第一个参数
18
19 */
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25
26 int main() {
27
28 // 获取当前的工作目录
29 char buf[128];
30 getcwd(buf, sizeof(buf));
31 printf("当前的工作目录是:%s\n", buf);
32
33 // 修改工作目录
34 int ret = chdir("/home/nowcoder/Linux/lesson13");
35 if(ret == -1) {
36 perror("chdir");
37 return -1;
38 }
39
40 // 创建一个新的文件
41 int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
42 if(fd == -1) {
43 perror("open");
44 return -1;
45 }
46
47 close(fd);
48
49 // 获取当前的工作目录
50 char buf1[128];
51 getcwd(buf1, sizeof(buf1));
52 printf("当前的工作目录是:%s\n", buf1);
53
54 return 0;
55 }
目录遍历函数
dirent结构体和d_type
代码:
1 /*
2 // 打开一个目录
3 #include <sys/types.h>
4 #include <dirent.h>
5 DIR *opendir(const char *name);
6 参数:
7 - name: 需要打开的目录的名称
8 返回值:
9 DIR * 类型,理解为目录流
10 错误返回NULL
11
12
13 // 读取目录中的数据
14 #include <dirent.h>
15 struct dirent *readdir(DIR *dirp);
16 - 参数:dirp是opendir返回的结果
17 - 返回值:
18 struct dirent,代表读取到的文件的信息
19 读取到了末尾或者失败了,返回NULL
20
21 // 关闭目录
22 #include <sys/types.h>
23 #include <dirent.h>
24 int closedir(DIR *dirp);
25
26 */
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 int getFileNum(const char * path);
34
35 // 读取某个目录下所有的普通文件的个数
36 int main(int argc, char * argv[]) {
37
38 if(argc < 2) {
39 printf("%s path\n", argv[0]);
40 return -1;
41 }
42
43 int num = getFileNum(argv[1]);
44
45 printf("普通文件的个数为:%d\n", num);
46
47 return 0;
48 }
49
50 // 用于获取目录下所有普通文件的个数
51 int getFileNum(const char * path) {
52
53 // 1.打开目录
54 DIR * dir = opendir(path);
55
56 if(dir == NULL) {
57 perror("opendir");
58 exit(0);
59 }
60
61 struct dirent *ptr;
62
63 // 记录普通文件的个数
64 int total = 0;
65
66 while((ptr = readdir(dir)) != NULL) {
67
68 // 获取名称
69 char * dname = ptr->d_name;
70
71 // 忽略掉. 和..
72 if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
73 continue;
74 }
75
76 // 判断是否是普通文件还是目录
77 if(ptr->d_type == DT_DIR) {
78 // 目录,需要继续读取这个目录
79 char newpath[256];
80 sprintf(newpath, "%s/%s", path, dname);
81 total += getFileNum(newpath);
82 }
83
84 if(ptr->d_type == DT_REG) {
85 // 普通文件
86 total++;
87 }
88
89
90 }
91
92 // 关闭目录
93 closedir(dir);
94
95 return total;
96 }
dup、dup2函数
代码:
1 /*
2 #include <unistd.h>
3 int dup(int oldfd);
4 作用:复制一个新的文件描述符
5 fd=3, int fd1 = dup(fd),
6 fd指向的是a.txt, fd1也是指向a.txt
7 从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
8
9
10 */
11
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <string.h>
18
19 int main() {
20
21 int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
22
23 int fd1 = dup(fd);
24
25 if(fd1 == -1) {
26 perror("dup");
27 return -1;
28 }
29
30 printf("fd : %d , fd1 : %d\n", fd, fd1);
31
32 close(fd);
33
34 char * str = "hello,world";
35 int ret = write(fd1, str, strlen(str));
36 if(ret == -1) {
37 perror("write");
38 return -1;
39 }
40
41 close(fd1);
42
43 return 0;
44 }
1 /*
2 #include <unistd.h>
3 int dup2(int oldfd, int newfd);
4 作用:重定向文件描述符
5 oldfd 指向 a.txt, newfd 指向 b.txt
6 调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
7 oldfd 必须是一个有效的文件描述符
8 oldfd和newfd值相同,相当于什么都没有做
9 */
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <fcntl.h>
16
17 int main() {
18
19 int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
20 if(fd == -1) {
21 perror("open");
22 return -1;
23 }
24
25 int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
26 if(fd1 == -1) {
27 perror("open");
28 return -1;
29 }
30
31 printf("fd : %d, fd1 : %d\n", fd, fd1);
32
33 int fd2 = dup2(fd, fd1);
34 if(fd2 == -1) {
35 perror("dup2");
36 return -1;
37 }
38
39 // 通过fd1去写数据,实际操作的是1.txt,而不是2.txt
40 char * str = "hello, dup2";
41 int len = write(fd1, str, strlen(str));
42
43 if(len == -1) {
44 perror("write");
45 return -1;
46 }
47
48 printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);
49
50 close(fd);
51 close(fd1);
52
53 return 0;
54 }
fcntl函数
代码:
1 /*
2
3 #include <unistd.h>
4 #include <fcntl.h>
5
6 int fcntl(int fd, int cmd, ...);
7 参数:
8 fd : 表示需要操作的文件描述符
9 cmd: 表示对文件描述符进行如何操作
10 - F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
11 int ret = fcntl(fd, F_DUPFD);
12
13 - F_GETFL : 获取指定的文件描述符文件状态flag
14 获取的flag和我们通过open函数传递的flag是一个东西。
15
16 - F_SETFL : 设置文件描述符文件状态flag
17 必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
18 可选性:O_APPEND, O)NONBLOCK
19 O_APPEND 表示追加数据
20 NONBLOK 设置成非阻塞
21
22 阻塞和非阻塞:描述的是函数调用的行为。
23 */
24
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 int main() {
31
32 // 1.复制文件描述符
33 // int fd = open("1.txt", O_RDONLY);
34 // int ret = fcntl(fd, F_DUPFD);
35
36 // 2.修改或者获取文件状态flag
37 int fd = open("1.txt", O_RDWR);
38 if(fd == -1) {
39 perror("open");
40 return -1;
41 }
42
43 // 获取文件描述符状态flag
44 int flag = fcntl(fd, F_GETFL);
45 if(flag == -1) {
46 perror("fcntl");
47 return -1;
48 }
49 flag |= O_APPEND; // flag = flag | O_APPEND
50
51 // 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
52 int ret = fcntl(fd, F_SETFL, flag);
53 if(ret == -1) {
54 perror("fcntl");
55 return -1;
56 }
57
58 char * str = "nihao";
59 write(fd, str, strlen(str));
60
61 close(fd);
62
63 return 0;
64 }