bin: 放命令
mnt:临时挂载点
~ 家目录
exit 退出
lib 库文件
u:属主
g:同组
o:其他人
a:所有人
-z : 判空操作
命令安装: apt install cmake
r w x -
读权限 修改权限 执行权限 没有权限
4 2 1 0
普通用户系统管理命令:
chmod 文字设定法(数字设定法) 文件名: 修改权限
文字设定法: u(g,o)-(+)r(w,x)
数字设定法:chmod 764 文件名
chmod 444 文件名
chown:改变用户所有者;
格式:chown 拥有者: 群组 文件名
eg:chown zzz : zzz text 改变text 的拥有者和群组为zzz。
chown zzz : text 改变text 的拥有者为zzz。
chown : zzz text 改变text 的群组为zzz。
touch .filename:建立简单的隐藏文件,在文件名前加“.”
ln filename1 filename2:硬链接(两个文件的ID编号相同)
ln -s 目录名 文件名:软链接 类型L(软链接只针对目录文件)
cp filename1 filename2:拷贝 1赋给2
./文件名 :执行此路径
printf" " :输出命令,输出字符串
touch . filename :创建普通的隐藏文件(只可以通过ls -a查看)
pwd:显示路径
cd: 切换路径 相对路劲和绝对路径
cd … :返回上一步
pwd:显示当前路径
ls:显示当前目录的那些文件
ls -l :显示详细信息
ls -a:显示所有文件(包括隐藏文件)
ls -i:显示文件编号
touch:创建一个简单文件
mkdir:创建一个文件夹
rm:删除
rm -r 文件夹(目录)名称 :删除由mkdir创建的文件夹目录
/home:返回home目录
cd -:在最近去过的地址之间进行切换
. :当前位置 当前路径
find: 在目录树中搜索指定的文件,也可以指定开始的搜索位置
grep:在一个已有的文件中搜索条件并打印
grep -i 忽略大小写( -c统计行数) “root” passwd
-i 忽略大小写
-c统计行数
wc:统计文件中单词个数(-w)、字符个数(-c)、行数(-l) ; wc -c按字符 -w按单词 -l 按行 filename
su: sudo su username 切换用户,没有指定用户名,则默认切换到管理员用户
/bin 存放常用命令(即二进制可执行程序)
/etc 存放系统配置文件
/home 所有普通用户的家目录
/root 管理员用户的家目录
/usr 存放系统应用程序及文档
/proc 虚拟文件系统目录,以进程为单位存储内存的映射
/dev 存放设备文件
/mnt 临时挂载点
/lib 存放库文件
/boot 系统内核及启动有关的文件
/tmp 存放各种临时文件,是所有用户均可访问的地点
/var 存放系统运行中常改变的文件, 如系统日志
Linux 下所有的东西都可以看做文件,Linux 将文件分为以下几种类型:
◼ 普通文件 ‘-’
◼ 目录文件 ‘d’
◼ 管道文件 ‘p’
◼ 链接文件 ‘l’
◼ 设备文件(块设备 ’b’ 、字符设备 ‘c’)
◼ 套接字文件 ‘s’
帮助手册man:
1命令 2系统调用 3库函数
cp(拷贝): 给出文件路径和名称 cp filename1 filename2 拷贝filename1并在当前位置创建filename2
当filename1和filename2皆存在时,拷贝filename1,以filename2为路径在filename2中创建名为filename1的文件
拷贝文件夹 : cp -r 文件夹1 文件夹2
rm -r:空与非空都可以删除
rm *:删除当前文件下的所有文件
rmdir:只能删除空文件夹
mv(重命名或移动文件:移动文件的同时可以改文件名):mv b.c a.c (改名字)将b.c改为a.c ;mv b.c a.c/c.c:将b.c移动进a.c并改名为c.c
移动文件夹不需要加 -r 操作 mv filename1 filename2:当filename2存在时,
以filename2为路径将filename1移动至filename2中并命名为filename1
cat(合并文件,往文件中写入数据,查看文件):cat >b.c(由键盘重新写入数据存入b.c ctr+D退出) cat b.c(将b.c的数据打印在屏幕)
cat a.c b.c >file(将a.c和b.c 的数据合并写入file中)
more(分屏幕显示):不可以反复查看,空格控制翻页
less:less filename 分屏显示,可以通过方向键反复查看,通过q退出
查看日志:
head(显示文件的头几行):100行 file head -20 file 显示前20行
tail:显示末尾 tail -20 file 显示末尾后20行
vi(文本编译器)模式:
命令:”esc“ 进入命令模式
插入:”i “由命令转入插入模式
末行:”:“进入末行模式
vi 文件名:只可以对普通文件操作,在内部操作 wq:保存并退出 q!:不保存退出
w:只保存不退出
1:创建.c文件 2:写入内容 3:退出
生成可执行文件: gcc -o 输出的文件名 执行源文件.c
./文件名:执行可执行文件
vim:
vi/vim 常用命令
- n dd //删除光标开始向下的 n 行
- n yy //拷贝光标开始向下的 n 行
- p //粘贴
- u //撤销上一次操作
- ctrl + r // 恢复上一次撤销操作
- r //替换一个字符
- shift + 6 //光标移动到当前行的行头
- shift + 4 //光标移动到当前行的行尾
- shift + g //光标移动到整个文本的最后一行
- gg //光标移动到整个文本的第一行
- n shift + g //光标移动到第 n 行
- d n shift + g //删除光标到 n 行的内容
- y n shift + g //拷贝光标到 n 行的内容
末行模式下的操作
- :w //保存文本
- :q //退出编辑
- :wq //保存并退出
- :q! //强制退出
- :w newfile //另存为
- :set nu //显示行号
- :set nonu //取消行号
- : set hlsearch //设置高亮搜索
- : set nohlsearch //取消高亮搜索
- : n,m s/oldstring/newstring //替换整个文本每行的第一个 oldstring
- :n, m s/oldstring/newstirng/g //替换整个文本所有的 oldstring
- /string //向下搜索 string
- ?string //向上搜索 string
F1(退出终端) F2-F6(打开终端)
find /绝对路径 -name(按名字搜索) filename:CTRL+c结束
I:管道 将前一个命令的输出结果作为后一个命令的输入 ls /bin I grep “pwd“
sudo su (root) :切换管理员 exit:退出
which 命令:which ls 查看命令在哪
shutdown -h now 立刻关机 halt 关机 init 0 关机
shutdown -r now 立刻重启 reboot 重启 init 6 重启
runleve: 查看系统运行级别 可以用 init 动态切换 0-6 共 7 个级别
◼ 0 关机
◼ 1 单用户模式
◼ 2 多用户无网络服务
◼ 3 完全的多用户 文本界面
◼ 4 未定义或 自定义
◼ 5 图形化界面
◼ 6 重启
init (0~6):切换模式
进程:一个正在运行的程序
系统管理进程依靠:PCB进程控制块 struct task_struct;
就绪,运行 ,阻塞
ps:显示进程 ps -e :所有的进程 ps -f:显示进程的所有信息(父子信息)
ps -ef:显示所有进程的所有信息
sleep seconds :前台睡眠 CTRL+c结束前台运行; sleep seconds &:在后台运行 jobs:查看后台运行
1:sleep seconds
2:ctrl+z:停止在前台运行的任务,转为stopped进程 jobs -l:显示详细信息
3:bg %任务号:转为后台进程运行 jobs -l:显示详细信息
4:kill掉
kill 进程ID:结束进程
pkill 进程ID:结束所有该进程ID
bg %任务号:将stop任务转进后台运行
fg %任务号:将后台任务转进前台运行
tar(打包,解包):tar cvf my.tar(创建新的文件名) filename1 filename2 -> 生成my.tar
压缩包 :gzip my.tar ->生成 my.tar.gz 压缩包; 解压释放 :gzip -d my.tar.gz 使my.tar.gz->my.tar tar xvf my.tar
一步解压:tar zxf my.tar.gz
将文件打包或者解包
◼ c 创建包文件
◼ f 指定目标为文件而不是设备
◼ v 显示详细过程
◼ t 显示包中的内容而不释放
◼ x 释放包中的内容
◼ z GNU 版本新加的,使得 tar 有压缩和解压的功能
添加新用户
useradd -m newname : 添加一个新的用户 newname passwd newname:修改设置密码 在管理员的身份
选项: -g 执行新用户的主组
-G 将新用户添加到副组
-s 指定新用户默认使用的 shell 终端
-d 指定新用户登录默认进入的目录
创建新用户需要管理员身份,创建新用户成功后,会在/home 下生成该用户的家目录。
删除用户
userdel 删除用户时,首先确保该用户没有登录。userdel 默认仅删除用户,不会删除家目
录及家目录中的文件,若想删除用户的同时移除家目录, 那么使用 userdel -r username。
gcc 分步编译链接
(1) 预编译 :
gcc -E main.c -o main.i
(2) 编译:
gcc -S main.i -o main.s
(3) 汇编:
gcc -c main.s -o main.o
(4) 链接:
gcc main.o -o main
一步链接:gcc -o main main.c
两步链接: gcc -c main.c // -o main.o
gcc -o main main.o
vim配置行号和缩进:
将以下内容加入 (管理员权限) redhat : /etc/vimrc deepin /etc/vim/vimrc 中,写入空闲地方就可以
set nu
set tabstop=4
set softtabstop=4
set shiftwidth=4
set expandtab
set smartindent
makefile管理工程,实现自动化的编译
make:
cmake:
调试程序 GDB
gdb调试:
两步:gcc -c hello.c -g —> 生成包含调试信息的中间文件
gcc -o hello hello.o
或者
一步生成: gcc -o hello hello.c -g
对象:正在运行的程序
调试版本: 包含调试信息的程序 -g gcc -o main main.c -g
l(+行号):(跳转)显示代码
b(break) 行号 :加断点
info break:查看断点信息
next(n):单目执行
c:继续执行遇见断点停下
p:打印
r:启动程序,运行程序,启动调试
display:打印,一直显示
delete+断点行号:删除断点
s:进入函数
bt:显示函数调用栈关系
finish(f):跳出函数
l 文件名.c :行号 :跳转到该文件的行
q:退出调试
&&&&&&&&&&&&&&&
HR重点
面试的三大问题
一 :库:预先编译好的函数方法的集合 add.c - add.o xx.o xxx.o -> libxx.a libxx.so
-L:指定库的路径。 -l:指定库的名字
/usr/lib:存放库文件
/usr/include:存放头文件
/usr/bin:存放可执行文件
静态库libfoo.a
共享库libfoo.so
ar crv libXXX.a filename.o
ldd main:显示main文件使用了哪些头文件
编译时:gcc -o main mian.c -L(库的路径) . -l (库的名字) foo
二: 静态库和共享库的区别:
main.c
静态库的生成与使用:
-L:指定库的路径。 -l:指定库的名字
第一步:先将需要生成库文件的所有“.c“文件编译成“.o”文件 //gcc -c add.c gcc -c main.c
第二步:使用 ar 命令将第一步编译的所有”.o”文件生成静态库,其中:ar crv libfoo.a add.o main.o
◼ c 是创建库
◼ r 是将方法添加到库中
◼ v 显示过程
会拷贝静态库中的函数方法在计算机中,占用过多空间
当更换新的静态库的时候,需要重新gcc编译,不包含静态库中的函数方法
共享库的生成与使用:
第一步:先将需要生成库文件的所有“.c“文件编译成“.o”文件
第二步:使用 gcc 命令将第一步编译的所有”.o”文件生成共享库
gcc -shared -fPIC -o libfoo.so add.o max.o 默认状态下优先使用共享库
只装载一份,大家一起共享,在内存中只有一份,占用空间小。
包含库内的函数方法,库更新后不需要重新gcc
三 :
通过哪个命令查看那些程序用了哪些共享库:
ldd main:显示main文件使用了哪些库文件
静态库不能查看,已经包含在了程序内部。
printf 函数输出问题:
printf 函数并不会直接将数据输出到屏幕,而是先放到缓冲区中,只有一下三种情况满
足,才会输出到屏幕。
1) 缓冲区满
2) 强制刷新缓冲区 fflush
3) 程序结束时
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[],char* envp[])
{
printf(“hello”);
//fflush(stdout);
sleep(3);
exit(0);
}
主函数参数介绍
int main( int argc, char* argv[], char* envp[])
(1) argc 参数个数
(2) argv 参数内容
(3) envp 环境变量
echo $path:打印path路径
export mystr:变成环境变量
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
重点
复制进程 fork:
fork 方法:fork+exec
pid_t pid = fork(); //fork结束后会复制一个一模一样的子进程,
getpid() : 获得自己的pid;
getppid():获得父进程pid;
pid_t fork(void): pid_t 是进程号类型,用来接收进程id号类型的数据;
函数返回类型为pid_t 类型,实质是 int 类型,Linux 内核 2.4.0 版本的定义是:
fork 函数会新生成一个进程,调用 fork 函数的进程为父进程,新生成的进程为子进程。
在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。
要注意的几个问题:
1) 父子进程并发运行的理解
2) 逻辑地址 物理地址
3) 写时拷贝技术 :数据写入时调动
进程PCB:其实是结构体:struct task_struct
wait()
僵死进程:子进程先于父进程结束,父进程没有获取子进程的退出码,子进程就变成僵死进程
所以当子进程结束后由init -> wait()获取退出码帮助系统删除子进程的PCB
文件描述符:系统调用
open
write
read
close
库函数:fopen、fread fclose fgets
操作文件的系统调用
(1)c 语言中文件操作回顾
(2)文件操作有关的系统调用
- int open(const char* pathname, int flags);//用于打开一个已存在的文件
- int open(const char* pathname, int flags,mode_t mode);//用于新建一个文件,
并设置访问权限 - 参数介绍:
- pathname:将要打开的文件路径和名称
- flags : 打开标志,如 O_WRONLY 只写打开
- O_RDONLY 只读打开
- O_RDWR 读写方式打开
- O_CREAT 文件不存在则创建
- O_APPEND 文件末尾追加
- O_TRUNC 清空文件,重新写入
- mode: 权限 如:“0600”
- 返回值:为文件描述符
- ssize_t read(int fd, void* buf, size_t count);
- 参数介绍:
- fd 对应打开的文件描述符
- buf 存放数据的空间
- count 计划一次从文件中读多少字节数据
- 返回值:为实际读到的字节数
- ssize_t write(int fd, const void* buf,size_t count);
- 参数介绍:
- fd 对应打开的文件描述符
- buf 存放待写入的数据
- count 计划一次向文件中写多少数据
- int close(int fd);
- 参数介绍:
- fd 要关闭的文件描述符
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
进程复制(fork)与进程替换(exec)
进程替换(execl, execlp, execle , execv, execvp, execve)
注意:替换的是已经运行的进程,使用新和成那个徐
(1) exec 系列替换过程:pcb 使用以前的只修改,进程实体更换
(2)以 ps 替换当前程序为例,介绍 exec 系统函数使用,注意该系列方法功能都一样,没有
区别,只是参数不同:
(3)execl(传路径) execlp(传名字) execle(需要传环境变量envp):
把参数罗列出来,传递给execl,所以他的参数个数是可变的
(4)execv execvp execve:把参数放进数组中,然后传递数组,所以参数是固定的
进程替换的作用:
进程产生:fork+exec
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
HR重点:
信号的基本概念: 信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的操作。
与信号有关的系统调用在“signal.h”头文件中有声明
常见信号的值,及对应的功能说明:
发送信号:kill()
响应信号:signal() SIGINT:ctrl+c 信号
1 默认方式响应 SIG_DFL
2 忽略 : signal(SIGINT,SIG_IGN);忽略响应
3 自定义:
void fun(){}
signal(SIGINT,fun);//signal(SIGINT,SIG_IGN);忽略响应
发送信号 – kill() 15 9(kill):不能设置响应方式,只能按照默认方式
kill() 可以向指定的进程发送指定的信号:
int kill(pid_t pid, int sig);
pid > 0 指定将信号发送个那个进程
pid == 0 信号被发送到和当前进程在同一个进程组的进程
pid == -1 将信号发送给系统上有权限发送的所有的进程
pid < -1 将信号发送给进程组 id 等于 pid 绝对值,并且有权限发送的所有的进程。
sig 指定发送信号的类型。
子进程结束以后发送给父进程的信号:SIGCHLD
wait(NULL);//结束僵死进程
bash机制:shell命令解释器总称 bash
mybash:模拟命令;
内置命令:作用于自身的命令 //chdir()
int main()
{
while(1)
{
printf("[stu@localhost ~]$");
fflush(stdout);//没有/n,用来刷新缓冲区
&&&&&&&&&&&&&&
strtok():分割,线程不安全
strtok(buff," ");//两个参数 字符串数组(不能是字符串常量)和分隔符(空格);
会有内存空间来记住上次的分割位置,内存空间的生存空间长于函数,在内存中只有一块空间
所有线程都可以访问,导致一个空间要记住多个不同的值,所以strtok不适用于多线程
注意: strtok分割不能在多线程中被使用;
系统提供了线程安全的分割方法: char *strtok_r(char *str,const char *delim,char**saveptr);
目标字符串; 分割格式; 指针地址
&&&&&&&&&&&&
HR重点:消息队列, 共享内存, 信号量数组,
ulimit -a: 来查看线程资源
ulimit -u(o,g) 1000 : 来设置线程资源
Linux内核能够创建多少线程:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
ipcs: 通过此命令可以查看 通信 情况;
ipcrm: ipcrm -s id号 可以用来删除信号量; -q 消息队列 -m 共享内存
进程间的通信方式(IPC): 管道,信号量,共享内存,消息队列,套接字
同步:
管道文件(大小永远为0):写入的数据在内存中,效率高 , 半双工工作方式(类似对讲机),管道自身具有同步
管道在内存中一格一个字节分配空间,有两个指针,一个
指向头,一个指向尾,
写入数据时:写入一个字节,头指针向后移动一格
读数据的时候:尾指针动,读一字节,尾指针后移一格
管道通信:对方不发数据和对方关闭管道,是两种情况
1)管道的写端关闭,读端返回0;
2)读端关闭,写端写数据会异常,直接终止程序
3) 没有发数据,读端堵塞
&&&&&&&&&&&&&
HR
进程间的通讯方式:1) 单工:只能一个方向发送数据;例如:收音机,和电台单收
2) 半双工:都具有读写功能,但是同一时间只能执行读或写 一种功能;例如:对讲机
3) 全双工: 两边都具有读写功能,可以同时执行这两种功能、 ;例如:打电话
.1有名管道
有名管道可以在任意两个进程之间通信
有名管道的创建:
◼ 命令创建: mkfifo fifo //创建文件名为fifo的管道文件
open打开管道文件 int fd=open(“fifo”,O_WRONLY); 只写打开文件
open成功的返回值为实际读到的字节数,失败返回-1
int n= read(fd,buff,127); 返回值为buff里的字节数
当n为0时则管道里没有数据,退出
write(fd,buff,strlen(buff)); //将strlen(buff)长度字节写入buff中
操作文件的系统调用
管道为空: 读操作阻塞
管道为满: 写操作堵塞
有名管道: 需要读写两个进程通知打开才可以。
◼ 系统调用创建
.2 无名管道
无名管道主要应用于父子进程间的通信
命令创建: pipe(int fd[2]); // fd[0]是读事件read,fd[1]是写事件write
int fd[2]; pipe(fd);
3 管道的特点
◼ 无论有名还是无名,写入管道的数据都在内存中
◼ 管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
◼ 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间
管道的实现
&&&&&&&&&&
面试问题
1)管道的工作方式: 半双工
2)管道的大小:永远为0,因为在内存中开辟空间,不写入磁盘中
3)管道的数据:写入管道的数据存放在内存中
&&&&&&&&&&&&&&&&&&&&&
HR重点:信号量
操作系统
#include
.1 信号量描述(就是代码中的信号灯)
信号量是一个特殊的变量,一般取正数值
它的值背后代表它可以访问的资源
二值信号量:0,1: 0代表不可以访问;1代表空闲可以访问。
计数信号量:0,fd: 0代表不可访问,fd代表数量,fd>=0,当fd=0时则不可以访问
信号量:是程序中的红绿灯,控制程序的推进速度和执行。
它的值代表允许访问的资源数目,获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作(p 原子减一)。当信号量值为 0 时,代表没有
资源可用,P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V 操作(原子加一)。
信号量主要用来同步进程。 先进行P操作,判断是否能通过,当为0说明P堵塞。
信号量的值如果只取 0,1,将其称为二值信号量。
如果信号量的值大于 1,则称之为计数信号量。
负数的绝对值:阻塞线程的个数;
pv操作获得的信号量的值没有意义;
临界资源:同一时刻,只允许被一个进程或线程访问的资源,通过信号量控制
对临界区的进入,来限制临界资源的访问。不允许访问临界区的代码
临界区:访问临界资源的代码段
&&&&&&&&&&&&&&&&&&&&&&&&
1):信号量的创建: int semget(key_t key, int num_sems,int sem_flags);如果存在则是获取信号量
key是整数值 ,信号量的数量, 标志位
key_t key: key是整数值,不相关的进程可以通过它访问同一个信号量。
semget返回值: semget函数返回的是信号量标识符,信号量id。
flags: 标志位,参数是一组标志,与open函数的标志非常相似。 IPC_CREAT:当没有信号量时创建,存在时返回-1
IPC_CREAT|IPC_EXCL : 当两者同时存在时,创建新的信号量,失败时返回-1;
2) 信号量的初始化和删除:int semctl(int sem_id,int sem_num., int command, …);
semget返回的id,信号量编号, 将要采取的动作
sem_num: 当需要用到成组的信号量时,就要用到这个参数,它一般取值为0,表示这是第一个也是唯一一个
信号量
… : 如果还有第四个参数,它将会是一个union semun结构,根据X/OPEN规范的定义,至少包含一下成员
union semun{
int val;
struct semid_ds buf;
unsigned short array;
}
注意: semun联合体必须由程序员自己定义。*
command: semctl 函数中的command参数可以设置许多不同的值,但只有下面的两个值最常用
1):setval:用来把信号量初始化为一个已知的值,这个值通过union semun中的val成员设置
其作用是在信号量第一次使用之前对它进行设置。
删除操作: 2): IPC_RMID:用于删除一个以及无需继续使用的信号量标识符。
semctl函数将根据command参数的不同而返回不同的值,对于setval和IPC_RMID,成功返回0,失败返回-1。
所以,在初始化信号量时需要我们自己定义这个联合体变量;
3): PV操作: semop函数: int semop(int sem_id, struct sembuf *sem_op, size_t num_sem_ops);
第一个参数: semge返回的semid,指向结构数组的指针, 描述信号量的个数
第二个参数,可以操作多个信号量:struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
}
sem_num:是信号量下标,除非使用一组信号量,否则它的取值一般都为0.
sem_op: 它的值是信号量在一次操作中需要改变的数值(使用一个非1的数值来改变信号量的值),通常只会
用到两个值,一个是-1,也就是p操作,它等待信号量变为可用;一个是+1,也就是V,它发送
信号,表示信号量现在可用
sem_flg: 标志位,通常被设置为sem_undo. 它使得操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在
没有释放该信号量的情况下终止,操作系统自动释放该进程持有的信号量
第三个参数: num_sem_ops : 信号量的个数
&&&&&&&&&&&&&&&
共享内存:include<sys/shm.h>
共享内存不单独使用,一般配和信号量使用
共享内存原理: 共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理
内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访
问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了
数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供
同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
共享内存的创建: int shmget(key_t key, size_t size, int shmflg); 没有时创建,存在时获取;
大小, 标志位
连接映射: void *shmat(int shm_id, const void *shm_addr, int shmflg);返回值为指针,指向共享内存空间
指向虚拟地址空间
断开映射: int shmdt(const void * shm_addr);
移除整个共享内存: int shmctl(int shm_id, int cmd, struct shmid_ds* buf);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&
消息队列:添加消息(设置消息类型且必须大于0) 获取消息(设置为0,不区分消息类型)
消息(结构体):struct mess {
long type; //消息类型:1,2,3 // !=0;
int a; //数据
char buff[128]; //你想要的数据
}
创建消息队列:msgget((key_t)1234,IPC_CREAT|0600);
添加消息: msgsnd(msgid,&dt,32,0);
获取消息: msgrcv(msgid,&dt,消息类型(1,2),0);
共享内存的存储: 存放在第三方处,使用时从第三方处取
消息队列的存储: 内核开辟空间,是一个一个的报文存储,每次只读取一个
管道数据的存储: 字节流存储,可以一次取完
报文传递是否线程安全:
&&&&&&&&&&&&&&&&&&&&&&&
线程的概念与实现方式
1.1 线程的概念
进程:一个正在执行的程序;
线程:线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。
线程之间共享同一个进程资源.;
线程函数:局部函数;并发运行,由于并发运行,所以发生输出错误的概率非常小;
四种线程同步方法:信号量,互斥锁,读写锁,条件变量
创建线程: pthread_create(pthread_t类型,属性,线程函数,参数) //函数形式: void* fun(void*)
pthread_exit() //退出线程,给主线程返回信息
char s; pthread_join(id,(void*)&s) //等待线程的结束
1.2 线程的实现方式
在操作系统中,线程的实现有以下三种方式:
◼ 内核级线程
◼ 用户级线程
◼ 组合级线程
1.3 进程与线程的区别
◼ 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
◼ 进程有自己的独立地址空间,线程共享进程中的地址空间
◼ 进程的创建消耗资源大,线程的创建相对较小
◼ 进程的切换开销大,线程的切换开销相对较小
localtime: 类似strtok_r;
ulimit -a: 来查看线程资源
ulimit -u(o,g) 1000 : 来设置线程资源
多线程下执行fork的情况: 子进程中fork只会执行自己所在的路径,不会执行父进程里
的其他线程
父进程创建了3个进程,子进程有几个进程?:1个线程
问题:
多线程下mutex锁:在fork之后,父进程的mutex锁也会被复制,复制后mutex
锁的初始状态由复制时父进程的锁的状态所决定
解决思路:
不要在临界区被使用期间进行fork,等待临界区使用结束后在进行fork;挑选在没有人
使用锁的情况下进行fork;
pthread_atfork方法:可以设置fork前调用加锁函数,fork后调用解锁函数;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
条件变量
条件变量的使用一般配和互斥锁共同使用;
初始化: pthread_cond_init
将线程添加到条件变量队列:pthread_cond_wait(): 阻塞,当条件合适时接触阻塞
唤醒:pthread_cond_signal() :唤醒一个线程
pthread_cond_broadcast():唤醒所有线程;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
信号量补充
信号量的创建: sem_t sem;
信号量的初始化: sem_init(&sem, 0, 1);
信号量传递; 能否进程共享; 初始化值;
P操作: sem_wait(&sem);
V操作:sem_post(&sem);
读写锁: 当未加锁时,都可以通过;当设置为读锁时,另一人读可以通过,写不可以通过; 当设置为写锁时为互斥访问,
另一个人都无法通过;
初始化:pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);
读锁:phread_rwlock_rdlock(pthread_rwlock_t *rwlock);
写锁:pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
解锁:pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
销毁锁:pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
报文传递:
系统V进程间通信机制:报文传递,共享内存,信号量三种机制
int ipc(unsighed int call, int first, int second ,int third, void *ptr,int forth);
第一个参数call为具体的操作码,定义在Linux内核 include/asm-i386/ipc.h中
操作码中由“SEM”开头的都是为信号量,“MSG”为报文传递,“SHM”开头
共享内存区
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
HR 重点:
实现线程方式: 用户级,内核级,组合模型;
用户级:创建开销较小,由线程库中的代码完成对它的管理,缺点是无法使用多处理器的资源
内核级:开销较大,但可以分配到其他内核中执行;利用多处理器的资源
混合模型:
Linux系统实现线程:实际上是以进程的形式实现线程,每个线程与父进程共享空间和资源
ps -L:可以查看进程信息,线程id;
线程安全:
同步: 互斥锁,条件变量,信号量,读写锁,
使用线程安全的函数: strtok,strtok_r,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
网络基本概念
1.1 网络: 把不同的主机链接起来就是网络;
网络是由若干结点和连接这些结点的链路组成,网络中的结点可以是计算机,交换机、
路由器等设备。
网络设备有:交换机、路由器、集线器
传输介质有:双绞线、同轴电缆、光纤
网络示意图
交换机
| | | (网线)
主机 主机 主机
互联网: 以网络为单位,把多个网络连接起来就构成了互联网,目前最大的互联网就是我们常说的因特网;
互联网示意图
路 由 器
| | |
交换机 交换机 其他网络
| | | |
主机 主机 主机 主机
◼ A 类:IP 地址范围为 0.0.0.0~127.255.255.255。
◼ B 类:IP 地址范围为 128.0.0.0~191.255.255.255。
◼ C 类:IP 地址范围为 192.0.0.0~223.255.255.255。
D 类和 E 类一般不使用。
IPV4地址示例: “192.168.31.1”
IPV4是32位的,用“.”分成4段,每个段8个位(0-255),用10进制表示。
IPV6地址示例: “2001:0db8:3c4d:0015:0000:0000:1a2f1a2b”
IPV6是64位,用“:”分成8段,每个段16位,用4个16进制数表示;
IP地址由网络号和主机号组成: 网络号相当于班级号,主机号相当于学号;
&&&&&&&&&&&&&&&&
使用命令“ifconfig”可以在 linux 查看自己的 ip 地址
Windows使用 ipconfig查看
127.0.0.1:自己给自己做测试
MAC地址: 在局域网中,硬件地址称为物理地址或者MAC地址,长度为48位,是固化在
计算机适配器的ROM中的地址,因此假定连接在局域网上的一台计算机的适配器
坏了我们更换了新的适配器,那么这台计算机的局域网的“地址”也改变了,虽然
这台计算机的地理位置没有发生变化 。
MAC地址与IP地址的区别: MAC地址就像是计算机的物理名字;IP地址可以更准确的定位计算机的地理位置
IP地址可以表示出物理位置的变化
3网络协议:
网络协议就是一组网络规则的集合,是我们共同遵守的约定或标准。常见的协议:
◼ HTTP:超文本传输协议
◼ FTP: 文件传输协议
◼ TELNET : 是 internet 远程登陆服务的标准协议。
◼ TCP : 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可
靠的、基于字节流的传输层通信协议
◼ UDP :用户数据报协议
◼ IP : Internet Protocol 简称 IP,又译为网际协议或互联网协议
◼ ICMP :因特网控制报文协议
◼ ARP : 地址解析协议,是根据 IP 地址获取 MAC 地址的协议
◼ RARP : 逆地址解析协议
端口:short int 16位 计算机上某个应用程序的代号或编号;
IP+端口:可以唯一确定一个进程;
知名: 80号端口(web) 3306号端口(mysql)
ping IP地址:测试
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
网络分层模型
OSI的7层模型与TCP/IP协议族体系4(5)层结构
OSI模型 TCP/IP模型
应用层--------
表示层-------- 应用层 //应用层负责处理应用程序的逻辑
会话层--------
传输层-------- 传输层 //进程间通讯机制
网络层-------- 网络层
数据链路层---- 数据链路层
物理层---------
为什么要分层:把大的问题划分为小的模块,每层去使用适合它的技术去实现
网络编程: 通过传输层提供的进程间通讯的能力;
传输层
TCP协议: 面向连接的(cli端connect连接)、可靠的(超时重传,应答确认机制,滑动窗口)、基于字节流的传输层通信协议; 通讯前先建立连接,结束后断开连接,类似打电话
流式服务:没有明确的起始和末尾,一次发送,一次接收
TCP还具有:去重,乱序重排功能,所以TCP成本要比UDP高
UDP协议:无连接,不可靠的数据报头; 类似发短信;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
重点:套接字通讯模式为全双工,发送与接收可以同时进行;互不影响
网络编程套接字tcp
ser 服务器 cli 客户端
套接字socket():创建连接 套接字socket():创建连接
指定端口: bind()指定ip端口; connect():建立连接,发起连接
listen(): 创建收听队列; send():给服务器发送数据;数据在发送缓冲区中
存放连接到服务器的客户端 recv(): 接收数据,读数据;在接收缓冲区中接收数据
accept(): 接收连接,得到新的描述符c close():关闭链接
recv(): 接收c上的数据,读数据;在接收缓冲区中接收数据
send():给客户端发送数据;在发送缓冲区中;
close():关闭链接;
判断cli关闭的条件: ser端的if(recv==0),则说明cli端关闭;
注意: 目前只有这一种方法判断cli关闭;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
重点:
TCP流式服务的粘包问题: 当有三个send和三个recv时,理想状态为一次send一次recv,但有可能出现连续send,
数据发送过快而第一次recv接受了全部数据,导致后两次recv发生堵塞,这就是粘包问题。
解决粘包问题的方法: 1) 避免连续send,每一次send和recv之后需要recv接收返回过来的send信息,方可进行下一次send;
2) 可以设置报头等其他标志来识别分辨数据;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
主机字节序列:分为大端字节序列和小端字节系列,不同的主机采用的字节序列可能不同;
大端字节序指:一个整数的高位字节存储在内存的低地址处,低位字节存储在内存
的高地址处;
小端字节序指:整数的高位字节序存储在内存的高地址处,低位字节则存储在内存
的低地址处;
在将数据发送到网络时规定整型数据使用大端字节序;对方接到数据后,根据自己的字节序进行转换;
网络字节序列: 大端字节序
主机字节序列转网络:
根据规定,在收到数据时,将数据转化为大端主机字节序: htons();h是主机,ton是网络;
套接字地址结构: IP和端口来唯一确定;
socket网络编程接口中表示socket地址的是结构体sockaddr:
通用的套接字地址结构:
struct sockaddr
{
sa_family_t sa_family;
char sa_data[4];
};
TCP协议: 面向连接的(cli端connect连接)、可靠的(超时重传,应答确认机制,滑动窗口)、基于字节流的传输层通信协议; 通讯前先建立连接,结束后断开连接,类似打电话
流式服务:没有明确的起始和末尾,一次发送,一次接收
TCP因为粘包的原因,所以比较适用于下载,文件传输等方面;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
重点:
三次握手,四次挥手
listen监听队列:创建两个监听队列,backlog为已完成三次握手队列的长度;
一个队列是未完成三次握手的队列, 另一个是已经完成三次握手的队列;
未完成握手的队列会接收到客户端发起的connect链接请求,connect返回成功则三次握手完成,失败则握手失败;
三次握手: connect期间,cli向ser发送请求连接的报文信息SYN seq=i;ser收到并确认后向cli发送确认信息ACK i+1和ser序列号 SYN seq=j;
cli收到ser信息并确认后又向ser发送确认信息ACK j+1;
在三次握手时发生的问题: 1)当网络不好的时候,ser发送的数据会出现丢包现象,则得重新send,则cli的recv就要一直等待阻塞;
2)当cli端有人恶意丢掉收到的所有数据包不send,导致ser端recv一直等待;
三次握手期间会受到的攻击:受到不断的恶意SYN报文,只发报文,不确认信息,导致监听队列被一直占用,
四次挥手: 当ser端close时会send给cli端结束报文FIN seq=n;cli端recv到结束报文后,给ser端send确认信息ACK n+1;过会cli端执行close,
cli端close时会send给ser端结束报文FIN seq=m;ser端recv到结束报文后,给cli端send确认信息ACK m+1;
三次挥手的情况: 当cli端给ser端回复确认信息ACK的同时恰好close发送FIN要关闭了,则有可能合成一步发送;
netstat -natp:查看网络信息已经端口号与PID;
抓包过程: 首先从ernest -laptop上执行telnet命令登录Kongming20的80端口,然后抓取这一过程中客户端和服务器交换的TCP报文段。
具体操作: $ sudo tcpdump -i eth0(enss033) -nt -S(详细显示信息) ‘(src 192.168.1.109 and dst 192.168.1.108) or (src 192.168.1.108 and dst 192.168.1.109)’
$ telnet 192.168.1.109 80
Trying 192.168.1.109...
Connect to 192.168.1.109
Escape character is '
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
HR重点:
为什么先程序结束的会出现time_wait状态: 程序结束了,但是端口连接内核还没有释放(6000端口),必须等到四次挥手结束后才会释放端口连接,
time_wait状态存在的意义: 先关闭的程序,在最后接收到对方的FIN信息后,给对方发送ACK确认信息,但是不一定保证对方一定会接收到ACK信息,
如果对方没有收到ACK信息,就会重新发送FIN信息,所以需要time_wait多等待一段时间;
1) 可靠的终止TCP连接
2) 保证让迟来的TCP报文段有足够的时间被识别并丢弃
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
UDP协议
UDP特点: 无连接,不可靠,数据报; DUDP适用于视频方面的场景
UDP 通讯流程:
ser cli
socket() socket()
bind() sendto()
recvfrom() recvfrom()
sendto()指定ip端口和地址 close()
close()
UDP数据报服务:
发送端 接收端
sendto() sendto() 应用层 recvfrom() recvfrom()
| | | |
UDP数据报 UDP数据报 传输层 UDP数据报 UDP数据报
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
HTTP协议
http协议:应用层,默认使用 80端口,在传输层使用的是tcp协议
Web服务器 www.baidu.com —DNS服务
|
http:80端口 connect ip:10.1.1.2
<----------------------- 浏览器 port: 80
ip:10.1.1.2 发送http请求报文
<-----------------------
回复http应答报文
----------------------->
send发送的是流式数据,一般默认是html文件;
输入服务器地址后,DNS服务找到对应的ip地址和端口号,浏览器得到地址后对服务器发起connect连接
短连接: 建立一个连接,只进行一次请求和发送;第二次使用需要重新建立连接
长连接: 建立一个连接,可以复用此连接,不需要再次建立连接
GET:是请求方法,表示客户端以只读的方式来申请资源,不对服务器产生然和其他影响
POST:客户端向服务器提交数据的方法,这种方法会影响服务器,服务器可能根据收到的
数据动态创建新的资源,也可能更新原有的资源;
应答状态码:
2** :200 ok,表示成功;
1**:表示continue,继续发送数据
4**:客户端出现错误
5**:服务器出现错误
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
I/O复用
HR重点:
I/O复用的作用: 使得程序能够同时监听多个文件描述符,对提高程序的性能至关重要;
使用I/O复用技术的情况:
1)TCP服务要同时处理监听socket和连接socket,这是I/O复用使用最多的场合;
&&HR重点:
为了使可以长期后台进行的方法:
1 fork() ->退出父进程
2 setsid() 创建新对话,需要提前准备好一个组员进程; 创建新对话时的id原来不能是组长,必须是普通组员
3 //fork() 使其失去会话首进程和进程组组长的身份,变成一个普通组员进程;、
4 chdir() 改变当前的工作路径
5 umask() 清除掩码; umask 0
6 close() 关闭一些多余的标识符
accept:是用来处理监听套接字的
recv:用来接收c上的数据;
服务器TCP socket,c加进来,c,
|
|
v
int n = select()
|
v
-1 失败; 0,超时;n>0,有n个元素的数据就绪
|
v
accept() -> c
|
v
recv(c),接收c上的数据
select流程:创建集合fdset,添加事件,设置超时时间,执行select,
select()的用法: #include<sys/select.h>
int select( int nfds , fd_set *readfds , fd_set *writefds , fd_set * exceptfds ,
struct timeval * timeout );
1) nfds参数指定被监听的文件描述符的总数。它通常被设置为select监听的所有文件描述符中的最大值加 1,
因为文件描述符是从0开始计数的;
2) readfds,writefds和exceptfds参数分别指向可读,可写和异常等事件对应的文件描述符的集合。需要向
集合中添加描述符,
fd_set结构体仅包含一个整型数组,该数组的每个元素的每一位标记一个文件描述符
通过下列宏来访问fd_set结构体中的位
FD_ZERO ( fd_set *fdset ); //清除 fdset的所有位
FD_SET ( int fd, fd_set *fdset) // 设置fdset的位fd
FD_CLR ( int fd , fd_set *fdset ) // 清除 fdset的位fd
int FD_ISSET ( int fd , fd_set *fdset) ; // 测试fdset的位fd是否被设置
3) timeout:超时时间;参数用来设置select函数的超时时间,他是
select缺点: 当描述符过多时,select需要轮循多次,效率太低,负担不了,支持1024个位,1个位代表一个描述符
poll: 是加强版的select,可以存储更多的描述符,支持更多的事件,内核处理依然以轮询为主;处理机制和select相差不大;
传参数可以是数组,所以可以处理的描述符要多于select
int poll( struct pollfd * fds , nfds_t nfds , int timeout); // poll系统调用成功返回就绪文件描述符的总数,超时返回0,失败返回-1
1)*fds:描述符数组;
struct pollfd
{
int fd; //文件描述符
short events ; //注册的关注事件类型
short revents; //实际发生的事件类型,由内核填充
};
2) nfds: 描述符个数
3) timeout: 超时
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
select poll和epoll的特点
1 select poll每次循环都需要向内核空间拷贝数据
2 内核实现: 轮训 O(n)
3 应用程序循环找到就绪描述符的时间复杂度为O(n) //知道有n个描述符
epoll: 解决客户端数量庞大,描述符多的场景
底层为红黑树;
1) epoll_create: 创建内核事件表,将文件描述符和事件存放在事件表中(红黑树)
epoll_ctl():添加描述符,一个描述符只向内核事件表中添加一次;时间复杂度为O(1)
2) 内核实现: 注册回调函数,
3)n= epoll_wait():获取就绪描述符,n为就绪描述符的个数,并把就绪的描述符返回填充到内核数组中
直接获取描述符;
int epoll_ctl ( int epfd , int op , struct epoll_event * event);
- epfd:内核事件表
2)op:操作; EPOLL_CTL_ADD:添加 EPOLL_CTL_MOD:改变 EPOLL_CTL_DEL:删除
3)event:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
EPOLL ONE SHOT 事件:即使使用ET模式,多线程环境中一个socket上的某个事件还是可能被多次触发,通过
EPOLLONESHOT来防止
ET :epoll专属模式,同一波数据提醒一次;高效模式,边缘触发 每循环一次只提醒一次,要求应用程序必须把数据读完,所以需要循环读取 将描述符fd设置成非堵塞
LT: 普通模式 , 水平触发 只要缓冲区中有数据,没处理完时就会一直提醒
I/O 复用方法:select poll(加强版select) Linux特有: epoll 共同:可以帮助我们检测多个描述符
select和poll的缺点: 由于描述符的增加导致当时设计跟不上时代发展 2, 只支持LT模式
POLL : 对poll来说是数组 ,采用轮训的方式O(n),遍历所有描述符找到就绪的O(n),
epoll: epoll_create //创建内核事件表 为了收集描述符和事件 (数据结构为红黑树, 就绪队列(容纳收集描述符的容器,实际上是链表)
epoll_ctl; //向内核事件表中添加,修改,移除,描述符
epoll_wait; // 获取就绪描述符,直接拿到的就是就绪的描述符,如果没有获得就绪描述符就会进入堵塞状态
好处: 每个描述符只需要向内核空间拷贝一次 2,内核:注册回调函数0(1) 3,可以直接拿到就绪描述符 0(1) 4,LT模式
select:可以监听多个描述符,但是一旦描述符数量过大就显得效率太低;
poll: 可以监听多个描述符,是加强版的select
epoll(linux) :
epoll与select poll的区别:
守护进程:一般在后台运行,不需要和用户交互且运行周期较长
编程流程:
会话: 会话首进程; getpid:3455作为会话首进程的id,用来作为会话id; getsid:会话id;
进程组: 组长进程;用组长的pid来标识进程组;
会话中包含进程组;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
libevent
libevent:是一个库,里面封装了select,poll和epoll的方法;以库函数的形式,封装了较为底层的
系统调用,给应用程序提供了一组更便于使用的接口;
1.设置配置:./configure prefix=/usr
2 make
3 make install
libevent: 信号,定时器, 注册事件,注销事件,事件循环,
#include<event.h>
Libevent处理的三大类事件: I/O事件, 信号和定时事件
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
Reactor模式
reactor模式要求主线程(I/O处理单元,下同)只负责监听文件描述符上是否有
事件发生,有的话就立即将该事件通知工作线程(逻辑单元,下同)。除此之外,主线程
不做任何其他实质性的工作。读写数据,接收新的连接,以及处理客户请求均在工作线程
中完成。
使用同步I/O模型(以epoll_wait()为例)实现Reactor模式的工作流程:
1) 主线程往epoll内核事件表中注册socket上的读就绪事件。
2) 主线程调用epoll_wait() 等待socket上有数据可读。
3) 当socket上有数据可读时,epoll_wait()通知主线程,主线程将socket可读事件放入请求队列。
4) 睡眠在请求队列上的某个工作 线程被唤醒,它从socket读取数据,并处理客户请求,然后往
epoll内核事件表中注册该socket上的写就绪事件;
5) 主线程调用epoll_wait()等待socket可写。
6) 当socket可写时,epoll_wait()通知主线程,主线程将socket可写事件放入请求队列。
7) 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
sockfd = socket
bind()
listen()
epoll_wait() ----------->存放描述符的数组或消息队列
| | |
线程1 线程2 线程3 多线程同步获取数组中的描述符
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
libevent
shell编程:快速,简单;解释型:需要解释器;开发效率较高,运行效率较低
脚本: vi my.sh
#!/bin/bash
echo"hello"
exit 0
1, 变量 :本地变量, 环境变量, 参数变量;
“ ” :弱引用;
‘ ’: 相当于c语言的字符串引用;
$: 取值符号;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
本地变量:
echo: 相当于printf;
read 变量名:从键盘获取值;
=:=左边是变量名,右边是变量的值;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
环境变量:
$PATH:直接由父进程继承而来的变量为环境变量;
KaTeX parse error: Can't use function '$' in math mode at position 10: :解释器的id; $̲PS1: 提示符 $0 :显示…:脚本id;
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
参数变量:
$1: 获取第一个参数; $2:获取第二个参数;
AWK:
使用格式限制:在文本文档中数据以列域的形式存储
把文件中的数据的整数部分提取出来放入新的文件中:awk -F.’{print $1}’ file > newfile
有些细节部分本人还未总结,后续还会有补充,也欢迎大家可以来补充!