LINUX
内核:C语言,开源
- 基本操作:cd~(回到家目录)(基本构造:家目录/home/user1)
- 文件类型:普通文件-,目录文件d
- 权限:r w x - chmod u/g/o
- 基本命令:cd touch mkdir pwd ls cp rm mv cat more less head tail vi vim ps(查看当前的进程) kill(结束进程) jobs &
- 文件权限的操作:查看权限(ls -l)
- 管理员:sudo su,
- 安装软件:apt install gcc
- 编译:(预编译,编译,汇编,链接)
- 库文件:预先编译好的集合{静态库(.a),共享库(.so)}
进程
-
一个正在运行的程序,动态,资源分配的基本单位
-
PCB(一个结构体):进程管理模块,进程的一个标志
-
进程状态:就绪,运行,阻塞
-
并发:同一时刻不能同时处理多个事件(但是在每个时间片都有对应的进程进行,时间片交替进行,一个处理器)
-
并行:多个进程同时进行(条件:需要多核处理器)
-
复制进程fork()函数:复制(包括复制的也是代码数据区和栈区等所以普内容)出来成为子进程(来自于父进程),属于同一个用户,返回值来唯一标识父子与否;父进程返回值是子进程的id号(大于0的值)则父进程在执行,如果返回值是0/-1那么是子进程在进行(子进程是从fork之后接着进行的,然后父进程和子进程携带着各自的fork()函数返回值,往下走可能是并行或并发的进程)
-
带换行符等的操作可以刷新缓冲区复制子进程过程情况输出:
-
#include<stdio.h> #include<stdlib.h>//exit(0) #include<unistd.h>//linux #include<string.h> #include<assert.h> int main(int argc,char* argv[],char* envp[]){ for(int i = 0;i<2;i++){ fork(); printf("A\n"); } exit(0); }//输出结果为6个A
-
不带换行符等的操作无法刷新缓冲区造成的复制子进程过程情况输出:
-
#include<stdio.h> #include<stdlib.h>//exit(0) #include<unistd.h>//linux #include<string.h> #include<assert.h> int main(int argc,char* argv[],char* envp[]){ for(int i = 0;i<2;i++){ fork(); printf("A"); } exit(0); }//输出结果为8个A //会打印出8个A,(主要原因是由于没有(\n换行符等刷新缓冲区作用无法输出到屏幕上)复制子进程过程中会将所有信息和运行空间复制包括父进程的缓冲区,所有会多出两个A)
-
父进程和子进程在不同的内存空间,虽然其对应的变量的地址(如:&n)相同,但是这只是逻辑地址是相同的,其实际存储的物理地址是不一样的,所以他们不在同一个内存空间
-
子进程执行完之后,会先删除对应空间,但会保留Pcb即产生僵死进程(子进程先于父进程结束,父进程没有获取子进程的退出码),虽然子进程已结束进程中还会存在子进程,只有退出码获得子进程才会真正结束;特别的:{若父进程先于子进程结束(此时不被称作为僵死进程),父进程会被bash获得退出码结束,我们只用管自己创建的子进程,此时子进程会被重新分配一个父进程(一般为1号进程)}
内存
- 物理内存:实际空间
- 虚拟内存:磁盘上的空间当作内存使用,以满足内存不够用的情况
缓冲区
由于内核处理数据,需要从缓冲区读取数据输出到屏幕上去解决办法有三个:
- printf()带换行符(\n)
- 缓冲区放满
- printf()下一行加fflush(stdout)
- 程序结束
main
- 构成:int main(int argc,char* argv[],char* envp[])//argc 参数个数argv参数名称 envp环境变量
文件
- Linux 一切皆文件
- Linux不区分二进制和文本
- 文件表(存储的文件的每个进程)初始就有3个进程(0,stdin)(1,stdout)(2,stderr)
- printf()实际上是底层调用(调用write的前提:有换行符,程序结束,缓冲区满了)的是内核中的write()即先放入缓冲区再从缓冲区读数据
- 父进程打开文件后,产生子进程,子进程也可以访问,共享struct file(pos的偏移量),每次访问pos都会++,但是文件struct node只有一个的![7HJ)SB1HLC3 ] ) P ])P%LF`LT](E:\图片\Saved Pictures\7HJ~)SB1HLC3~ ])P])P%LF`LT.png)
替换进程(exec系列)
- 实现原理:fork一个子bash+exec(ps)(用ps替换该子进程),所以会出现ps的父进程是bash(命令解释器(shell)之一)
- 只要打开一个终端就会产生一个bash
信号
- 定义:通知进程产生了某种事件
- 进程的三种处理信号(sigal(),kill()发出信号)方式:1)默认;2)忽略;3)自定义
- 子进程先于父进程结束后,内核会给父进程发送一个信号(17号),
命令解释器
- 用户通过命令解释器(shell)与内核进行交互,目前常用的有bash
- 命令分为(1,内置命令;2,普通命令)
- 内置命令:例cd, bash 直接实现->调用函数实现
- 普通命令:通过产生子进程fork+exec()将子进程替换
进程间通信(IPC)
-
实现机制:管道,信号量,共享内存,消息队列,套接字
-
管道:1)有名管道;2)无名管道 (两进程之间可通过管道文件进行信息传输)
-
用mkfifo创建有名管道文件
-
打开管道后,在内存中分配空间
-
向管道中写入数据,实际是写入内存中
-
读取,也是向内存中读取,管道文件的大小用完永远是零
-
写端关闭,读端read()返回值为0
-
如果读端关闭,写端,写入数据会产生异常,(收到信号SIGPIPE)
-
通信方式:以半双工方式进行
-
半双工:可进行单工和全双工,单某一时刻只能处于一个状态
-
单工:一直使用单个方式进行
-
全双工:同时进行
-
管道文件在内存中的实现:(头指针,尾指针),头指针负责写入数据;尾指针负责读取数据;尾指针追上头指针就相当于读空数据;头指针追上尾指针就意味着写满了
无名管道
- 主要用在父子间通信,而有名可以在任意两个进程之间
信号量
-
一个特殊的变量,一般取正数值,(pv操作),p操作,需要对信号量的值进行原子减一,当信号量为0的时候,p操作会阻塞,释放资源时候,需要对信号量的值进行加1,该操作为v操作;信号量主要用来同步进程,信号量的值如果只取0或1,将其称为二值信号量,如果信号量的值大于1,则称为技术信号量
-
临界资源:同一时刻,只允许被一个进程或线程访问的资源
-
临界区:访问临界资源的代码段
-
中途非正常退出进程,产生的信号量不会主动消失,必须手动删除信号量
-
ipcs命令查看信号量和消息队列
-
ipcs -s 查看信号量
-
ipcrm -s +信号量id可以删除信号量
共享内存
- 一般都会用到信号量
- 实现进程间的实时通讯
消息队列
进程
- 一个正在运行的程序,资源分配的基本单位
线程
-
进程内部的一条执行路径,(序列),调度的基本单位
-
创建线程:pthread_creat();(pthread.h)且编译的时候在后面要加上-lpthread
-
pthread_exit()(退出线程)
-
pthread_join()(阻塞,待子线程结束,再向下执行)
-
线程的实现:
- 1)用户级线程(线程库管理,创建开销小,不能使用多个处理器);
- 2)内核级线程(内核管理,创建开销大,可以利用多个处理器);
- 3)组合级线程(用户和内核组合进行)
-
Linux是内核级线程实现方式,能实现并行处理线程
-
线程中常遇到的问题:
-
如果主线程产生多个子线程,且子线程调用的函数中传的参数(i)在主线程被改变的话,由于传的是地址,所以每个进程都能访问到,也就导致在子进程中输出该参数的值会与预想的结果不一样
-
预想结果:一共三组,各组都有(0,1,2,3,4)
-
实际结果:可能是(0,0,0,0,4)
-
void* fun(void* arg){ int index = *(int*)arg; for(int j = 0;j<3;j++){ ptrintf("index=%d\n",index); sleep(1); } } int main(){ pthread_t id[5]; int i = 0; for(;i<5;i++){ pthread_creat(&id[i],NULL,fun,&i); } for(i = 0;i<5;i++){ pthread_join(id[i],NULL) } }
-
-
显示线程的id
-
线程同步:
- 信号量;互斥锁;条件变量;读写锁