我的学习方法是:Linux系统编程(看pdf笔记) + Linux网络编程 + WebServer
01P-17P Linux相关命令及操作
-
cp -a dirname1 dirname2
复制目录cp -r dirname1 dirname2
递归复制目录 1 到目录 2这里-a 和-r 的差别在于,-a 是完全复制,文件权限,改动时间什么的也完全相同。
-
more filename
和cat
差不多,但是对于大文件查看很强势 -
head -n filename
查看文件前 n 行 -
tail -n filename
查看文件后 n 行 -
ln -s file file.s
创建一个软链接(相当于win的快捷方式),最好使用绝对路径 -
ln file file.h
创建一个硬链接,文件和硬链接的 Inode 是相同的,每个文件都有唯一的 Inode(硬链接一改都改,类似于cpp的引用) -
find命令
-type按种类,-name按名字 -
grep 命令:找文件内容
,与ps配合使用ps aux | grep 'cupsd'
-
gzip 和 bzip2
都是压缩命令,配合tar使用,rar压缩需要安装rar。 -
ifconfig
查看网卡信息
18P - 24P Vim相关(一刷不看)
25P-27P gcc相关
- gcc中间文件及步骤:
28P-37P 动态库和静态库
- 静态和动态的优劣:静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,但是这样的好处就是静态加载的速度快;使用动态库会将动态库加载到内存,10 个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存。(如何理解动态和静态:静态的写在源代码里的函数,相对 main 函数偏移是一定的,链接时,回填 main 函数地址之后,其他源代码 里的函数也就得到了地址,动态则用plt代替,之后加载的时候再替换掉 )
- 编译器只能隐式声明返回值为 int 的函数形式:int add(int ,int ); 如果函数不是返回的 int,则隐式声明失效,会报错:在使用自己编写的静态库时,需要在Main中加入函数声明 。但这个方法需要库的使用者知道库里的函数,完事儿一个一个加到代码里,不太行,解决方法:使用头文件加载静态库:这样防止多次展开静态库带来额外开销。
38P-40P GDB调试(跳过)
41P-46P Makefile(跳过)
47P-51P 系统调用open
- 系统调用是内核提供的函数,库调用是程序库中的函数;
open()
函数出错时,程序会自动设置 errno,可以通过 strerror(errno)来查看报错数字的含义 以打开不存在文件为例(如果成功fd会返回打开文件所得到对应的 文件描述符)
52P-53P 系统调用read/write(复制命令)
- 系统调用(write/read)和库函数调用(fgetc/fputc)的区别:read每次写n个字节,会疯狂进行内核态和用户态的切换,所以非常耗时。fgetc/fputc,有个缓冲区,预读入缓输出机制。
54P 文件描述符
- 文件描述符是指向一个文件结构体的指针。
- 在Linux中,文件描述符是一个非负整数,用于标识一个进程正在使用的文件或者其他输入/输出资源。每个进程都有一个文件描述符表,其中存储了该进程打开的文件描述符。标准输入、标准输出和标准错误输出分别使用文件描述符0、1和2。其他文件描述符从3开始递增。文件描述符可以用于读取、写入、关闭文件以及进行其他文件操作。标准输入输出一般指的就是键盘输入/输出;
55P 阻塞和非阻塞
- 阻塞/非阻塞是设备文件、网络文件的属性(常规文件没有阻塞的概念)。
- 比如读取
dev/tty
(这也是个文件),如下写法就是阻塞的:
/* 使用阻塞的read读取终端(标准输入输出)*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>int main(){char buf[10];int n;n = read(STDIN_FILENO, buf, 10);if(n < 0){perror("read STDIN_FILENO");exit(1);}write(STDOUT_FILENO, buf, n);return 0;
}
56P-57P 系统调用fcntl
fcntl(file control)
是一个系统调用,用来改变一个【已经打开】的文件的访问控制属性- 函数声明
int fcntl(int fd, int cmd, ... /* arg */ );
cmd表示命令,比如下面这句代码将阻塞改为非阻塞:其中F_SETFL设置,F_GETFL获取
flags = fcntl(STDIN_FILENO, F_GETFL); //获取 stdin 属性信息if(flags == -1){perror("fcntl error");exit(1);}flags |= O_NONBLOCK;int ret = fcntl(STDIN_FILENO, F_SETFL, flags);if(ret == -1){perror("fcntl error");exit(1);}
58P 系统调用lseek
lseek
函数通常用于随机访问文件,例如读取文件中的某个特定位置的数据。- 函数声明:
#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);
fd
是文件描述符,offset
是偏移量,whence
是偏移量的起始位置。whence
可以取以下三个值之一:
- `SEEK_SET`:偏移量相对于文件开头。
- `SEEK_CUR`:偏移量相对于当前位置。
- `SEEK_END`:偏移量相对于文件末尾。
返回值是新的文件偏移量(-1表示错误)
- 常用作用:使用lseek 获取文件大小、配合truncate拓展文件大小
- 使用lseek获取文件大小的例子:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "Usage: %s <filename>\n", argv[0]);exit(1);}int fd = open(argv[1], O_RDONLY);if (fd == -1) {perror("open error");exit(1);}off_t size = lseek(fd, 0, SEEK_END);if (size == -1) {perror("lseek error");exit(1);}printf("File size: %ld bytes\n", size);close(fd);return 0;
}
59P 传入传出参数
传入参数: 1. 指针作为函数参数。 2. 同常有 const 关键字修饰。 3. 指针指向有效区域, 在函数内部做读操作。
传出参数: 1. 指针作为函数参数。 2. 在函数调用之前,指针指向的空间可以无意义,但必须有效。 3. 在函数内部,做写操作。 4。函数调用结束后,充当函数返回值。
传入传出参数: 1. 指针作为函数参数。 2. 在函数调用之前,指针指向的空间有实际意义。 3. 在函数内部,先做读操作,后做写操作。 4. 函数调用结束后,充当函数返回值。
60P 目录和inode
- inode:它是文件系统中的一个数据结构,用于存储文件的元数据信息。每个文件在文件系统中都有一个唯一的
inode
号码,通过这个号码可以访问文件的元数据信息,例如文件的权限、所有者、大小、创建时间、修改时间等等。 - 在Linux文件系统中,文件名和
inode
号码是分开存储的。文件名存储在目录中,而inode
号码存储在文件系统的inode
表中。 - 一个文件主要由两部分组成,dentry(目录项)和 inode。所谓的删除文件,就是删除 inode,但是数据其实还是在硬盘上,以后会覆盖掉。(参考某些数据恢复措施)