linux最基础概念
1.终端的提示符
zhu@linux:$
zhu:代表当前的用户名 Linux:代表计算机名 :$中间是当前的路径
2.用户切换
要想操作管理员用户的文件,必须在指令前加 sudo。
两个用户直接可以相互的切换 su 用户名
3.可查询的特殊命令
帮助指令 man --- 他是一个显示帮助的分页工具
man 1 指令
man 2 库函数
man 3 标准系统函数
find 功能在指定的路径下去搜索指定的文件名
find 路径 -name 文件名
grep 功能在指定文件里去搜索指定关键字
grep -n “关键字” 文件名
4.ls -l 显示文件的详细信息
第 1 位代表文件的类型
文件的类型:
- 代表普通文件
l 代表链接文件 --- 快捷方式
c 代表字符设备
d 代表目录文件
b 代表块设备
p 代表管道文件
s 代表套接字
第 2-4 位代表当前用户对文件所拥有的权限
权限:r 代表可读 --- 4
w 代表可写 --- 2
x 代表可以执行 --- 1
第 5-7 代表同组用户对该文件所拥有的权限
第 8-10 代表其他用户对文件所拥有的权限
用户名
文件的大小 --- 是以字节为单位
文件的创建时间
文件名
5.路径
相对路径:就是相对于当前路径来说的
绝对路径:就是相对于根目录来说的
6.主函数传递参数
int main(int argc,char *argv[])
argc:代表你传递的参数的个数
argv:代表传递的具体的那一个参数
argv[0] --- 执行这个程序的命令本身
argv[1] argv[2] …… 就是后边传递的参数
7. .C 文件从编写到执行经历了 4 个过程
编译预处理 --- 处理#开头的 宏定义 条件编译
gcc -E xxx.c -o xxx.i
编译 --- 主要就是处理语法错误的
gcc -S xxx.i -o xxx.s
汇编 --- 把汇编给翻译成二进制文件
as xxx.s -o xxx.o
链接 --- 就是找库 --- 找你调用的函数库 --- 一般调用的函数都是默认自动去找到的,如果 要是第三方库那么就需要自己手动添加库名。
gcc xxx.o -o xxx
其实正确的写法是 gcc xxx.c -o xxx -l 库名
8.什么是库以及他的作用?
其实库就是很多函数封装的一个集合,他就是方便你开发调用,比如你现在买了一块 STM32 的开发板,你想使用这块板子做开发,那么你就需要调用厂商提供的函数,但是厂商他有不想把源码给你,这个时候就诞生一个库,他把你要使用的一些函数,做了一个封装,生成了一个文件,这个文件就叫库文件,你使用的时候只需要添加他的库文件即可,库最大程度的保护开源的源代码。
库他有分为静态库和动态库
9.静态库和动态库的区别
- 首先就是后缀名不同,静态库是以.a 结尾的,动态库他是以.so 结尾的
- 静态库:由于其内容被完整包含在可执行文件中,会导致可执行文件体积较大。动态库:可执行文件本身不包含动态库的代码,仅包含对动态库的引用,因此文件相对较小。
- 静态库:如果静态库有更新,所有使用该库的程序都需要重新编译链接。动态库:动态库的更新相对简单,只需替换库文件,而使用它的程序无需重新编译。
- 静态库:因为代码已经嵌入到可执行文件中,所以对运行环境的兼容性要求相对较低。动态库:在运行时加载,需要确保系统中存在所需版本的动态库,对运行环境的依赖较强。
10.静态库的制作
库是有源文件.c 而来的
制作静态库的指令
1.先把所有的.c 文件变成.o 文件 gcc -c xxx.c //默认生成.o 文件
2.把.o 文件制作成.a 文件 --- 静态库的文件
ar -rc libxxx.a xxx.o
使用的时候就需要加库文件
gcc xxx.c -I./include libxxx.a ---- 生成了一个 a.out
一旦 a.out 生成了那么此时 libxxx.a 对于 a.out 来说已经没有用了
此时 a.out 已经不在依赖于任何一个文件独自运行了。
11.动态库的制作
把所有的.c 生成.o
gcc -c xxx.c -fpic
把所有的.o 文件生成.so 文件
gcc -fipc -shared xxx.o -o libxxx.so
动态库生成之后,还需要把动态库给复制到/usr/lib 或是/lib 下
因为动态库在执行的时候会去/usr/lib 或者是/lib 下去动态的找
gcc xxx.c -o xxx -I./include -lxxx lxxx 是库
动态库他是在你程序运行的时候动态的去/usr/lib 或者是/lib 下去找
所以你不能把这个库随意的删除或者是移动
12.makefile 他是什么,他可以干什么?
makefile 他是一个工程管理工具,主要就是管理工程的,管理工程的编译,尤其是管理大的工程, 几万个文件甚至是几十万个文件,Makefile 都可以轻松的应对。
makefile 他是一门专用语言,他有自己的语法规则,其实每一门语言都有自己的语法规则。
make 他编译的指令是在终端输入 make 他会自动的去当前的目录下找makefile/Makefile 如果找 不到就报错,make 他是根据时间戳去编译文件的
意思就是说,如果你在规定的时间内没有改变文件,那么 makefile 他就不会编译,就是说我刚才以 及执行过 make 他也编译生成了可执行文件,在你下次再进行make 的时候如果这个时间段你没 有修改或者是打开工程里的任何一个文件,那么make 他是不会再次编译的。
13.文件操作
文件 IO 他是没有缓冲的,而标准 IO 他是有缓冲的,其实缓冲他就是一个空间,他是用
来保存你操作文件的时候写入或者是读取内容的一个暂时存放的一个位置。
行缓冲:就是写满了一行就会自动的换行
无缓存:就是没有缓冲,关闭你是写入还是读取的内容会直接到你想要的地方。
全缓存:把这个缓冲区给写满了,就会触发缓存。
14.标准文件API
fopen --- 按照指定的方式去打开这定文件
FILE *fopen(const char *pathname, const char *mode)
fclose int fclose(FILE *stream)
fread 块读
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
fwrite 块写
size_t fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream)
ftell 计算文件的大小 --- 他是根据当前光标所在的位置进行计算的
long ftell(FILE *stream)
fseek 移动光标的位置
int fseek(FILE *stream, long offset, int whence)
fprintf --- 格式化输入
int fprintf(FILE *stream, const char *format, ...)
fscanf --- 格式化输出
int fscanf(FILE *stream, const char *format, ...)
fscanf --- 格式化输出
int fscanf(FILE *stream, const char *format, ...)
rewind --- 移动光标的,只能把光标移动到文件的开头
void rewind(FILE *stream)
fgets ---- 从文件里获取 size-n 个字符或是是遇到了\n,获取是文件结束了
char *fgets(char *s, int size, FILE *stream)
fputs ---- 把一个字符串写入到文件里
int fputs(const char *s, FILE *stream)
fgetc --- 从文件里获取单个字符
int fgetc(FILE *stream)
fputc --- 把单个字符写入到指定文件里
int fputc(int c, FILE *stream)
getchar --- 从键盘上获取单个字符
int getchar(void)
14.非缓冲区文件api
open --- 按照你指定的方式打开指定的文件
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode); --- 创建文件的
close --- 关闭打开的文件
int close(int fd);
read --- 从文件里读
ssize_t read(int fd, void *buf, size_t count)
write --- 把内容写入到文件里
ssize_t write(int fd, const void *buf, size_t count)
lseek --- 和 fseek 是一模一样的
int lseek(FILE *stream, long offset, int whence)
15.获取时间的API
函数功能:获取日历时间
函数原型:time_t time(time_t *tloc)
函数功能:获取格林威治时间
函数原型:struct tm *gmtime(const time_t *timep)
函数功能:获取本地的时间
函数原型:struct tm *localtime(const time_t *timep)
函数功能:以字符串的形式显示当前的时间
函数原型:char *asctime(const struct tm *tm);
函数功能:将日历时间转换成本地时间
函数原型:char *ctime(const time_t *timep);
函数功能:格式化显示时间
函数原型:size_t strftime(char *s, size_t max, const char *format, const struct tm *tm)
16.获取文件属性的函数有三个
函数功能:获取指定文件的属性
函数原型:int stat(const char *pathname, struct stat *statbuf)
函数功能:--- 获取指定文件的属性
函数原型:int fstat(int fd, struct stat *statbuf)
fd:文件描述符 --- 你提供的这个文件必须要使用 open 打开
函数功能:获取指定文件的属性,他可以查看链接文件的属性
函数原型:int lstat(const char *pathname, struct stat *statbuf)
17. 时间编程shell
和时间相关的指令
date --- 显示当前的时间
date -R --- 显示当前的时间只是格式不同
date -u --- 显示格林威治时间
cal --- 显示日历时间
cal 月份 年份
目录
1.目录操作的基本函数
就是在程序里动态的去操作目录的一些基本函数,就类似于在终端实现的指令。
mdkir --- 动态的创建目录
int mkdir(const char *pathname, mode_t mode)
参数 1:创建的目录名字
参数 2:创建时候给指定的权限
chdir --- 切换到指定的路径下
int chdir(const char *path)
参数:你要切换的路径
getcwd --- 获取当前的路径
char *getcwd(char *buf, size_t size)
参数 1:获取得到路径所存放的位置
参数 2:第一个参数的最大值 --- 最大规定了是 255
chmod --- 修改文件或者是目录的权限
int chmod(const char *pathname, mode_t mode)
参数 1:目录或者是文件的名字
参数 2:给指定的权限
rmdir --- 删除目录文件
int rmdir(const char *pathname)
参数:你要删除文件的名字
2.目录操作函数
操作目录的第一步也是先打开目录,最后还要关闭目录文件。
opendir --- 打开指定的目录文件
DIR *opendir(const char *name)
参数:就是打开的目录文件名
closedir --- 关闭指定的目录文件
int closedir(DIR *dirp)
参数:就是 opendir 函数的返回值
readdir --- 从目录文件里去读取文件的信息
struct dirent *readdir(DIR *dirp)
参数:就是 opendir 函数的返回值
seekdir --- 移动目录流的位置
void seekdir(DIR *dirp, long loc)
参数 1:就是 opendir 函数的返回值
参数 2:就是 telldir 函数的返回值
telldir --- 获取当前目录流的位置
long telldir(DIR *dirp)
参数:就是 opendir 函数的返回值
进程
进程的概念
1.什么是进程
进程就是一次程序的运行过程,他是个动态的,是一个活的,运行的状态。
2.进程的表示方法
系统是根据进程号来区分不同的进程的,进程号他是唯一的,是不可能有重复
的,就好比人类的身份证号一样。
3.进程的关系
父进程:就是创建进程的那个进程就是父进程,使用 PPID 表示
子进程:被创建出来的那个进程就是子进程,使用 PID 表示
4.进程的状态
执行态:正常使用 CPU 给他分配的时间片,就是在运行当中。
就绪态:就是一切准备就绪,就等待 CPU 给他分配时间片
阻塞态:就是等待某些条件的产生
5.进程特性
动态性:他是动态运行的状态 并发性:同一时刻,可以运行很多个不同的进程
异步性:他们直接有相互的制约性
独立性:每一个进程的空间都是独立的,都是互不干扰的
6.进程空间资源的分配
每运行一个进程系统都会默认的给他分配 4G 虚拟内存,这 4G 的虚拟内容被
分为了,代码段,栈区,堆区,数据段。
7.特殊进程
0 号进程:就是祖先进程,就是最初启动的那个进程
1 号进程:也被叫做 init 进程,他主要就是负责管理和收养孤儿进程
僵尸进程:他已经不在为操作系统做任何贡献,但是他还占用这系统的资源没有释放
孤儿进程:就是父进程先于子进程退出了,没有进程去管理他了,此时该进程会有 init 进程来管理和收养。
进程的 API 函数
获取 PID
getpid() ---- 获取自己的 Pid 号
getppid() ---- 获取自己父进程的 Pid 号
创建进程的方法有两种
fork() ---- 创建一个子进程
pid_t fork(void)
返回值:成功 pid=0 在父进程中返回的是子进程的 pid 号
失败是 -1
子进程会复制父进程的所有的资源,然后从 fork 下开始就无法确定是谁先运行的,他们的空间是
独立的是互不干扰的。
vfork() ---- 创建一个子进程
pid_t vfork(void)
返回值:成功 pid=0 在父进程中返回的是子进程的 pid 号
失败 -1
特点:一旦子进程创建成功一定是子进程先运行并且子进程会阻塞父进程,直到子进程运行结束了,父进程才会得到运行,并且他们的资源是共享的,就是子进程先使用资源,然后父进程在接着使用子进程使用过的资源。
wait--等待
等待进程的退出让后做清理工作,任何一个进程退出都会去做清理工作,避免子进程出现僵尸进程 ,次函数会阻塞当前的进程
pid_t wait(int *wstatus)
参数:一般写 NULL
waitpid --等待(阻塞可选)
等待进程的退出让后做清理工作,任何一个进程退出都会去做清理工作,避免子进程出现僵尸进程,这个函数你可以选择是否阻塞
pid_t waitpid(pid_t pid, int *wstatus, int options)
参数 1:等待指定哪一个子进程退出
参数 2:保存进程退出状态,大部分是写 NULL
参数 3:决定是否阻塞父进程
0:阻塞当前的进程 --- 父进程
waitpid(pid,NULL,0) == wait(NULL);
WNOHANG:写这个宏就代表阻塞,就是说不会阻塞当前的父进程
exit
exit(0) ---- 进程正常退出的状态
_exit(0) ---- 进程正常退出的状态
exit 和_exit 的区别是:exit 在进程退出的时候会先去清理缓存里的内容
_exit 他不会清理缓存里内容,直接就会结束进程 ,一般大部分使用的是 exit。
exec 函数族
都是在一个进程里去启动另一个程序,但是他会覆盖当前的进程
execl
int execl(const char *pathname, const char *arg, ... /* (char *) NULL */);
参数 1:你要执行的程序的路径
参数 2:执行此程序的执行
参数 3:后边的参数就是执行这个程序给传递的参数
最后一个参数必须是 NULL
execlp
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
参数 1:你要执行的程序,如果是系统的默认的程序那么你就不用写路径 ,他会默认的去系统的环境变量上去找,如果要是执行自己写的程序一般是写要路径的
参数 2:执行此程序的执行
参数 3:后边的参数就是执行这个程序给传递的参数
最后一个参数必须是 NULL
system --- 系统函数
int system(const char *command)
参数:系统所支持的指令
进程之间通信
1.进程之间通信原因?
信息传递 ---- 进程和进程之间他们也是需要信息传递和交流的。
协同工作 ---- 某些功能是需要进行之间相互协同工作的
所以进程之间是需要要进行通信的,就好比人类之间进行通信是一样的。
- 数据的传输
- 资源共享
- 通知事件
- 进程控制
不管你是以上的哪一种情况都是需要进程之间通信的
2.进程间通信方式有六种
- 管道
- 信号
- 消息队列
- 共享内存
- 信号量
- 套接字
3.具体的方法介绍
3.1 管道
管道他主要就是用于有血缘关系之间的进程进行通信的----父子进程----无名管道
3.1.1 管道分类
无名管道:就是没有名字的管道 --- 父子之间
有名管道:就是有名字的管道 --- 任意之间的进程进行通信
3.1.2 管道的构成
管道他分为两个端:这两个端是固定的,一个是读端一个是写端
读端:只能用来读取数据,不能从这个端口进行写数据
写端:只能用来写数据,不能从这个端口进行读数据
管道他又有读阻塞和写阻塞,就是说如果你只写不读,那么当数据写满之后就会发生写阻塞,就是这个管道写满,就不能再写了,再写就会发生写阻塞。
如果你只进行读操作,那么数据全部读取之后就会发生读阻塞。数据一旦读走之后就不会再有数据在管道里了。 所以读端和写端必须同时存在才有意义。要有读有写不然就会发生阻塞
3.1.3 管道的通信方向
半双工
意思就说同一时刻只能有一个进程进行读或者是写,就好比对讲机。
父子进程使用管道进行通信的时候,需要要保证父进程和子进程操作的是同一个管道,
是先创建管道还是先创建子进程?---- 肯定是先创建管道,然后子进程会复制这个管道。
3.1.4 管道的API
函数功能:创建无名管道
函数原型:int pipe(int pipefd[2])
函数头文件:#include <unistd.h>
函数参数:pipefd[2]:他是存储管道的两个端口的,是一个整形的数组
pipefd[0]:读端 --- 其实他就是文件描述符
pipefd[1]:写端 --- 其实他就是文件描述符
函数返回值:成功返回 0 失败负数
注意:一旦创建成功了那么这两个端是出于打开的状态
读操作和写操作就可以通过 read 和 write