Day1
Linux的体系
- linux内核
内核管理着系统的操作包括 文件系统管理(不同文件系统之间的数据交流)
网络管理(管理着进程间的通信)
进程管理(多进程之间的相互调度)
硬件管理(控制不同硬件设备之间的运转)
内存管理(管理着物理内存和虚拟内存之间的映射)
2、shell(命令行解释器)
命令:ls cp mv rm。。。。。
解释:在命令行提示符输入指令时,bash告诉内核去bash所在的文件夹(/bin)里找与指令同名的绿色文件(可执行文件),找到进行操作,找不到命令不存在。
Day2
- 网络基础
IP
IP(网络协议) 作用:唯一的标识一台主机
格式:点分十进制的形式 (32个字节)
组成形式:网络地址(高地址)+主机地址(低地址)
A类(第一个最高位是0) 1网络地址+3主机地址
00000000~01111111 00000000~11111111
范围 0.0.0.0~127.255.255.255 (1.0.0.0~126.255.255.254)
B类(第一个最高位是10) 2网络地址+2主机地址
10000000~10111111 00000000~11111111
范围128.0.0.0~191.255.255.255
C类(第一个最高位110) 3网络地址+1主机地址
11000000~11011111 00000000~11111111
范围:192.0.0.0~223.255.255.255
D用来组播的,E留在以后用的
0.0.0.0是一个随机的IP,一般不用----用1.0.0.0
以127开头的IP一般是回环地址----126 回环:虚拟的地址,自己跟自己通信
以255结尾的IP一般是组播地址----254
子网掩码
判断两台主机能否通信,将IP和子网掩码进行“与”操作(&有0全为0,都为1才为1)
问题1
- ->192.168.2.1 ------>255.255.255.0
- -->192.168.2.2 ------>255.255.255.0
得出网段
A:192.168.2.0
B:192.168.2.0 同一个网段A和B可以进行通信
问题2:
- ->192.168.2.1--->255.255.255.0
- ->192.168.3.2--->255.255.0.0
得出网段
A:192.168.2.0
B:192.168.0.0 不相等分析谁给谁通信
B向A发送消息
B子网掩码 & A的IP:192.168.0.0,和B的网段是一样的,所以B可以给A发送消息
A向B发送消息
A子网掩码 & B的IP:192.168.3.0--->此时进行ping操作,超时,主机地址不可达。
子网掩码设置:将网络地址全部弄成最大,将主机地址全部弄成最小
C类的子网掩码:255.255.255.0
网关:
是网间连接器
一台主机通向另一台主机的IP地址
一个主机里面有很多的网关,但是会有一个默认的网关
默认的网关的作用?
想要通信,但是找不到别的网关,就回去找默认网关。
默认网关的设置:C类IP 192.168.x.1(x就是主机处于哪一个网段)变的是第3个x
DNS服务器
是域名解析器 域名如www.baidu.com 百度
域名和IP是绑定的,这种绑定关系会存在DNS服务表里面。
域名可以有多个,对应一个IP。但是一个IP只能对应一个域名
2.SHELL命令
命令名称 选项 参数
Grep printf main.c
选项和参数可省,命令名称不可省
用户相关的命令
(1)切换用户
sudo su root sudo 在$用户下进行一些#用户的操作
Su root 切换到超级用户
Su 切换用户身份 目录不发生改变
Su -切换之后,会默认回到家目录
exit退出超级用户
(2)添加用户
Sudo adduser 用户名
添加之后会默认的添加到/home文件夹下,但是/home/用户名也是一个家目录
通过vi/etc/passwd 如果出现在这个配置文件里面,才代表成功添加新用户
(3)删除用户
Sudo deluser 用户名
Sudo rm-rf aaa强制删除
Sudo deluser --remove-home aaa一次性删除
删除了aaaa,但是文件夹仍然存在,因为有些配置文件在内核,没有删除干净
env 环境变量
直接在命令行提示符输入env 会出现很多的环境变量,环境变量全是大写
$:取环境变量的值
PWD打印路径 PWD:/home根目录下的home
echo $ shell echo:shell的标准输出
被双引号括起来的内容,输出的时输出路径(软转义),先去找能够匹配的内容,找到之后,直接输出内容
被单引号括起来的内容,输出的时候(硬转义)直截了当的输出输入的东西
加上双引号和不加双引号没有区别(除了空格和反斜杠)
添加环境变量
./指明执行当前文件下面的可执行文件
如果不再这个目录下面执行,就需要更改PATH
路径之间通过冒号分隔
Export是设置PATH
export PATH=/home/linux/22122/day2:PATH
PATH和等号之间没有空格
这是一种临时修改的方式
永久修改 vi ~/.bashrc
修改配置文件 ~/.bashrc
Cat
Cat 在终端上查看文件内容
Head/tail
从头/尾查看文件内容 默认十行,指定行数 head/tail -n 文件名
More/less
More:按百分比的形式显示
Less:比较纯净,没有命令行提示符
Grep查找 (文件内部的内容)
Grep +参数+ 查找的内容 + 文件名
参数:
n:显示查找的内容,并显示所在的行数 grep -n查找的内容只有一行
v:显示除了查找之外的所有内容
c:显示所要查找的内容一共有多少行
Find命令(目录里面的内容)
Find+文件名+路径(绝对路径)
如果文件不存在,会显示目录下面的所有内容
find查找的内容有一行
Chmod
Ls -l查看文件,会出现文件的权限和属性
-rw-rw-r--
第一个-:代表文件的属性 。Linux一共有7中文件属性
b:块设备文件
c:字符设备文件
d:目录文件
-:普通文件
l:链接文件
s:套接字文件
p:管道文件
第一个rw-:代表了用户的权限,转换成二进制就是110
第二个rw-:代表了用户组的权限,转换成二进制是110
第三个r--:代表了其他用户组的权限,转换成二进制是100
当普通文件权限给到最大,就算是普通文件,也会变成绿色
刚创建的文件,权限是664。
为什么是664?
满文件权限777,减去文件掩码,得出来的结果奇数位减1,偶数位不变,就是新文件的权限。
文件掩码 umask
更改文件掩码 umask +数字
目录的最大权限是777,文件的最大权限是666
文件掩码存在:防止文件权限过大。
符号标记法:
用户:u
用户组:g
其他用户组:o
所有:a
ln命令
链接就是一个指向源目的地的一条路径,简单的认为,相当于windows的快捷方式
链接文件分为两种,一种是软连接,一种是硬链接
软连接的创建方式
创建出来的soft1.c是grade的软连接文件
创建软连接的时候,一般用绝对路径
Ln -s 绝对路径/文件 软连接文件名
创建出来的软连接文件的内容和源文件的内容是一样的
软连接文件和源文件的inode不一样,也就是说软连接文件和硬链接文件是两个文件
硬链接文件
Ln 路径/文件名 硬链接文件名
tar
C:打包
X:解包
V:可视化
F:指明打包的文件
Z:指明压缩的工具是gzip
J:指明压缩的工具是bzip2
c 2.c 3.c
Tar -cvf FILE.tar *.c 打包
Tar -xvf FILE.tar -C 路径 解包
调用压缩工具位gzip
Tar -cvzf FILE.tar.gz *.c 打包+压缩
Tar -xvzf FILE.tar.gz -C 路径 解包+解压
调用压缩工具位bzip2
Tar -cvjf FILE.tar.gz *.c 打包+压缩
Tar -xvjf FILE.tar.gz -C 路径 解包+解压
Wc
会显示指定文件的大小:行数,单词的个数,字符数
Day3
1.SHELL特殊字符
*:匹配任意的长度
?:匹配的是一个长度
[1-9]:匹配1到9里面任意一个长度
[157]:匹配的是1,5,7里面任意一个长度
[^157]:匹配除了1,5,7之外的长度
管道
作用:将前一个的输出,当作后一个的输入
Command1 | command2 |......|command N
Fortune-zh 说古诗
Cowsay 牛说话
Cowsay fortune-zh
Fortune-zh | cowsay 将说古诗当作输出,不让他输出在终端上,让他输出到牛里面。
用find命令查找文件,然后用grep输出指定的字符串
Find ./ *.c 查找文件 Grep -n printf ./*.c
Find ./ *.c | grep -n printf
想要通过管道实现,需要借助xargs
Xargs:将前一个的输出,格式化成一行,当作参数传递给后一个命令。
为什么需要格式化成一行?
因为find查找到的文件可能有多行,传递给grep的时候不知道传递哪一行,所以需要弄成一行传递给grep。
命令置换
符号:` `
格式: command1 `command2`
将命令2的输出当作命令1的参数。
输入输出重定向
默认的输入源和输出源
Stdin:键盘
Stdout:显示屏
输入重定向 用 < ,一般默认不写
输出重定向:用 >
为什么需要输出重定向?
快速的将一个文件里面的内容保存到另一个文件里面。
>:保存正确指令的内容
>>:保存正确指令的内容,但是是追加模式。
(只保存正确的,如果有错误的,会清空test.txt的内容)
- :保存错误指令的内容
- >:保存错误指令的内容,并追加模式
(只保存错误的,如果有正确的,会清空test.txt的内容)
&>:保存正确和错误的
&>>:保存正确和错误,并追加。
2.Shell脚本
为什么搞脚本? 看懂别人写的脚本并且能自己写脚本
Shell脚本? 用shell命令和其他的一些字符串组合出来的文本
书写shell脚本的步骤
-
- 确定文件的后缀 .sh(脚本文件)
- 写脚本
- 更改文件的权限,给用户加上可执行权限
- 直接运行
- ./*.sh 不需要经过编译
shell脚本的书写格式
交代了解释器的位置,第一行永远都是这个
注释要从第二行去写,不能写在/bin/bash的后面,否则会直接将第一行全部认为是解释器的位置。
在终端上书写shell命令,写一次就需要和内核交互一次,而对于shell脚本,可以写很多命令,然后统一交给内核处理
shell的基本用法
Shell其实是一个弱定义,并没有明确的数据类型区分。只需要直接定义变量
Shell的变量类型
(1)普通变量
1.变量的定义
变量名
2.变量的赋值
变量名=常量 (直等)
3.用变量的值
${ 变量名} $变量名
(2)位置变量
对于数组是引用下标,shell的位置变量,同样是引用下标
(3)系统预定义变量
系统已经定义过了,格式一般就是$加上一个其他的字符。
$@: 输出在命令行提示符输入的字符,不包括脚本文件
$*:和$@一样
$#:统计在命令行提示符输入多少个字符,不包括脚本
$?:输出上一行命令的执行码。0正确执行,1,错误执行
(4)环境变量
env
更新环境变量:export PATH=路径:${PATH}
取消环境变量:unset 环境变量
SHELL程序和语句
Shell语句:由shell命令组成的
Shell程序:由shell语句组成的
(1)Shell语句
1.说明性语句
#!/bin/bash 指明解释器的位置
2.功能性语句
(1)输入变量
read 变量名 变量名(变量名可以直接输入,不需要进行数据类型的定义)
Read相当于C语言里面的scanf
Echo相当于C语言里面的printf
(2)算术命令
+ - (\*)这个是shell 的乘法算术命令 / %
Expr 变量1 算术命令 变量2
Expr 和变量以及变量和算术命令之间都需要空格
(3)测试命令
Test
假设变量是 n1 n2
格式1:test n1 n2
格式2:[ n1 n2 ]
测试整型:n1 n2
大于等于 n1 -ge n2
大于 n1 -gt n2
小于 n1 -lt n2
小于等于 n1 -le n2
等于 n1 -eq n2
不等于 n1 -ne n2
测试字符串:s1 s2
相等 s1 = s2
不相等 s1 != s2
S1长度是否为零 -z s1
S1长度是否不为零 -n s1
测试文件
-f 测试文件是否为普通文件,并且文件要存在
-d 测试文件是否是目录文件,目录也要存在
-L 测试文件是否是链接文件,文件也要存在
-r 测试文件是否存在并且具有可读权限
-w 测试文件是否存在并且具有可写权限
-x 测试文件是否存在并且具有可执行权限
测试文件1是否比文件2新 file1 -nt file2 (通过文件创建的时间去比较)
测试文件1是否比文件2旧 file1 -ot file2
测试文件是否存在且长度不为零 -s file1
(4)逻辑命令
与 and -a [a -lt 0 -a `expr a %2` ]
或 or -o [ a -gt 0 -o a le 10 ]
功能性语句
选择
If [ 表达式 ]
Then
命令表
Elif [ 表达式 ]
Then
命令表
Elif [ 表达式 ]
Then
命令表
.....
Else
命令表
Fi
从键盘获取一个字符串,判断字符串的文件属性,如果是目录文件,就在里面创建一个test.txt,更改用户的权限为可执行并输入字符。如果是普通文件,就更改用户权限为可执行并输入字符。
(4)Case选择
Case 命令表 in
模式)
命令表
;;------------->break
模式)
命令表
;;
。。。。。
*)------------------>default
命令表
;;
Esac
- 用case实现对于成绩的管理
- 从键盘输入两个字符串,根据文件的时间,将新文件的内容放到旧文件里面(cp)
- 从键盘获得年月,输出对应的天数
- 用case实现计算器(expr)
Day 4
1.控制语句
For in
for 格式 in 格式表
do
命令表
done
For in每变化一次i自动会移动一个位置,shell不会,要手动添加
脚本写法
C写法
While循环
While[ $i -ne 100 ]
Do
命令表
Done
while死循环
While [ 格式表 ]
Do
命令表
Done
格式表里面只要不是完整的测试命令,都算死循环。
2. c高级
Shell函数
函数名() Add()
{ {
函数体; Echo “aaaa”
} }
调用方式
1:采用命令置换的方式
Value=`函数名 参数1 参数2 .。。。。参数N`
参数给的方法
- 给固定的值
- 从键盘获取
- 位置变量获取
- 直接调用(不需要通过命令置换的方式)
函数名 参数1 参数2.。。。。。参数N
递归函数
递归:自定义一个函数,然后再函数体内继续调用自身
第一步:函数从什么地方开始
第二步:函数从什么地方结束
第三步:函数怎么变化
3.结构体
描述事物的时候,单一的数据类型无法描述。需要复杂的数据类型。
结构体的定义
Struct AA(结构体名)
{
数据类型1 参数1;
数据类型2 参数2;
。。。。。
};
现在定义的东西叫数据类型
Struct AA --->数据类型
定义结构体变量
结构体类型 变量名。
结构体指针
数据类型 * 指针名 = &变量名。
&:当变量名已经是一个地址的时候,不需要&,反之需要。
4.字节对齐
用来计算结构体大小
Struct AA
{
Char name[25];
Int a;
short a;
}------->36字节
计算机会按照最大的一个sizeof(数据类型)来分配
Struct AA
{
Int a;
Short b;
Double c;
}; ------->16个字节
不能按照sizeof(double)去分配,因为32OS,默认是4字节对齐,也就是说,,最大只能按照4个字节去分配。
Struct AA
{
Char a;
Int b;
Short c;
};--------->12个字节
Struct AA
{
Char a;
Shrot b;
Int c;
} ------>8个字节
一般在定义结构体的时候,为了节省内存空间,定义的时候从字节小的开始,一直到字节大的
5.共用体
定义
Union 共用体名
{
数据类型 变量名;
};
*通配符(各种文件名
C高级
递归函数
递归:自定义一个函数,然后再函数体内继续调用自身
第一步:函数从什么地方开始
第二步:函数从什么地方结束
第三步:函数怎么变化
结构体
描述事物的时候,单一的数据类型无法描述。需要复杂的数据类型。
结构体的定义
Struct AA(结构体名)
{
数据类型1 参数1;
数据类型2 参数2;
。。。。。
};
Struct AA --->数据类型
定义结构体变量
结构体类型 变量名。
结构体指针
数据类型 * 指针名 = &变量名。
&:当变量名已经是一个地址的时候,不需要&,反之需要。
字节对齐
用来计算结构体大小
Struct AA
{
Char name[25];
Int a;
short a;
}------->36字节
计算机会按照最大的一个sizeof(数据类型)来分配
Struct AA
{
Int a;
Short b;
Double c;
}; ------->16个字节
不能按照sizeof(double)去分配,因为32OS,默认是4字节对齐,也就是说,,最大只能按照4个字节去分配。
Struct AA
{
Char a;
Int b;
Short c;
};--------->12个字节
Struct AA
{
Char a;
Shrot b;
Int c;
} ------>8个字节
一般在定义结构体的时候,为了节省内存空间,定义的时候从字节小的开始,一直到字节大的
共用体
定义
Union 共用体名
{
数据类型 变量名;
};
Day5
1.结构体嵌套
2.指针函数 函数指针 函数指针数组
指针函数
是一个函数,指针代表着函数的返回值是一个指针类型
char *strcat(char *dest, const char *src);
函数指针
是一个指针,指向的是一个函数
函数是实现某些功能模块,实质上是存放在内存当中的一段二进制代码,函数名相当于是函数的入口地址。
定义:
函数指针=函数名;
函数指针=&函数名;
这两种方式都能够引用函数名,不过用第一种 直接用指针名代替函数名。第二种 *指针名代替函数名。
函数指针的写法t a,
定义函数指针 int add(inint b)
1.需要知道指针的数据类型(无法直接得到),需要通过指针的指向类型(函数)(int (int ,int ))推出来指针的数据类型
2.给函数的数据类型加*,加指针名(int *p(int ,int)),然后根据优先级,p先和()结合变成函数,然后才和int *结合变成指针,就成了指针函数,所以要给*和P加小括号--->int (*p)(int,int)
3.写法:
(1) Int (*p) (int ,int ) = add;----->p = add ,p 是指针变量,可以用来存放地址,add是函数的入口地址,所以入口地址可以赋值给指针变量p
(2)Int (*p)(int ,int ) = &add p = &add
函数/数组的数据类型=指针指向的数据类型
3.枚举
4.堆区操作
1.申请空间
#include <stdlib.h>
void *malloc(size_t size);
作用:堆区开辟空间
参数:开辟空间的大小
没有void类型的变量,但是有void *类型的指针,只是一个定义,用来当作万能指
void *可以变成任何类型的 指针类型
2.清空空间
#include <string.h>
void *memset(void *s, int c, size_t n);
作用:清空一片空间
参数1:要清空的空间的首地址
参数2:想要用什么样的字符来清空
参数3:清空多大的空间
3.释放空间
#include <stdlib.h>
void free(void *ptr);
作用:清空空间
参数:清空空间的地址
Void *指针:系统并不知道需要开辟什么数据类型的空间,根据自己的使用需要。
5.make工程管理器
将所有的文件管理起来。通过make命令,一次进行编译
1.c------>1
-
- 预处理 gcc -E 1.c -o 1.i
- 编译 gcc -S 1.i -o 1.s
- 汇编 gcc -c 1.s -o 1.o (生成机器码)
- 链接 gcc 1.o -o 1 1(可执行文件X)
1.c 2.c
-
- Gcc 1.c 2.c 适用于多个.c文件只有一个main函数
执行make命令需要写一个makefile
写法:
目标:依赖
[TAB键]命令表
目标:想要生成什么样的文件
依赖:生成这个文件所需要什么东西
命令表:依赖变成目标的过程
1.i:1.c
[TAB键]Gcc -E 1.c -o 1.i
Day6
Make
1.单文件编译
(Vim makefile创建一个makefile文件 Make 执行)
(目标文件和初始创建*.c文件需要编辑,过程文件*.o和目标文件需要删除,ls里的文件有makefile和.c文件)
Make是一个指令,用来管理.c文件的,但是在执行make指令的时候,需要makefile
目标:依赖
[TAB] 命令表
目标:想要生成的文件
依赖:生成目标文件需要什么文件
命令表:依赖怎样变成目标(以main.o为例 gcc -c main.c -o main.o)
第一种makefile
第二种makefile
2.多文件编译
- 分开写.c文件
- 完成头文件的书写
c语言引用头文件有两种方式 以stdio.h为例
- #include <stdio.h> 会默认的去/usr/include 里面去寻找头文件
- #include “stdio.h” 会去用户的工作路径下找头文件,被双引号引起来的头文件一般是用户自己书写的头文件
3.书写头文件的步骤
以test.h为例
#ifndef _TEST_H_
#define _TEST_H_
(翻译:如果没有定义test.h,那么久定义一个test.h)
//1.程序中所有能够用到的头文件(C库(c语言的库函数) 头文件)(被<>包起来的头文件)
//2.枚举
//3.结构体/共用体
//4.宏定义
//5.函数声明
#endif
为什么开头需要这样写?防止头文件被重复命名
引起段错误的方式:1.非法操作内存空间2.越界
Haha是一个假目标,只是用来执行删除的命令,并不需要依赖,但是要用.PHONY:haha,说明
4.Makefile嵌套
Make是目录下面的工程管理,所以makefile嵌套至少需要两层目录。
需要用到的目录:src obj include bin
需要用到的工程文件:makefile
5.添加变量
- 定义变量
- 将变量添加到环境变量中去
(1)自动变量
$@ 目标文件
$^ 所有的依赖文件
$< 第一个依赖文件
(2)预定义变量
CC 默认的编译器
CFLAGS 默认的编译器选项
——————————————————————————————————————————————————————————————————————————————————————————
- 创建4个目录:
- 创建基本文件
需求:有三个.c文件,一个.h文件保存共有引用和成员
e1.c e2.c e3.c e00.h
- 用vi编辑器编辑.c .h文件
3.1先考虑程序中可能会用到的函数,直接去.h文件中包含头文件
3.2编辑.c文件实现功能
- 写src路径下的makefile文件
- 写obj路径下的makefile文件
- 编写大makefile文件
- 在终端执行make
定义是把函数的实现过程也写出来,而声明只写函数第一行加号