一、进程的创建和回收
(一)进程的概念
1、进程!=程序
程序是静态的,而进程是动态的,进程是程序执行的一种状态。
2、进程和程序的区别
1)进程控制块中包含进程的属性
2)程序在磁盘里面,堆栈都是在内存中,程序运行起来都在内存中
3)进程的运行在内存(RAM)中,手机内存指的是运行内存64G,电脑内存指的是硬盘(ROM)
4)初始化的全局变量在数据段,没有初始化的全局变量放在BSS中
代码段存放可执行文件,机器码
5) static声明的变量不在栈中,和全局变量放在一起
栈:参数,返回值,局部变量
堆:malloc
6)一个进程会被分为不同的区域
7)进程控制块(pcb)
PID、进程用户、进程状态、优先级、文件描述符表
3、进程的类型
1)交互进程:最常用的进程,在前台运行且在shell下可以控制
2)批处理进程:提交到作业队列以便顺序执行
3)守护进程:一直在后台运行,不能用shell命令控制
4、进程的状态
(二)进程常用的命令
1、查看进程信息
1)ps当前状态下的进程
2)ps -e当前状态下Linux所有的进程
3)ps -elf Linux下进程的详细信息
4)进程状态
5)进程标志
6)ps下的目录含义
PID:进程ID
CMD:进程的名称
NI:进程优先级
PRI:进程优先级
SZ:占用内存
C:占用的CPU利用率
7)ps -elf|grep PID
查看某个进程
2、top实时查看进程信息
1)翻页shift+'<'(前翻页)或'>'(后翻页)
2)top -p PID查看某个进程
3)ctrl+c退出
3、/proc查看进程的某个目录
4、nice命令:打开一个进程,设置优先级
越nice优先级越低
1)一般nice只能调高
2)只有root才能设置为负值
4)renice改变优先级
5、后台进程
6、前台和后台的区别
./test在前台运行:
正常情况下ctrl+C可以结束进程,下次该进程对应的代码还是前台运行
6、ctrl+Z结束前台运行转为后台
ctrl+Z:此时程序进入停止状态
7、jobs查看后台进程,这时进程已经在后台
8、bg将挂起的进程在后台运行
9、./test &也可使后台进程运行
10、fg将后台进程重新挂到前台运行
fg+序号进程又重新挂到前台
*(三)创建子进程
1、子进程的概念
在Linux下除了0号进程,其他的进程都是由别人创建的
2、创建子进程
让我们的进程创建子进程
1)代码
子进程B的代码和A的代码相同
但子进程B只执行程序中fork函数之后的代码
2)打印结果
父进程子进程分别打印
3)如何让父子进程分别执行
(四)父子进程
1、子进程只执行fork之后的代码
2、父子进程执行顺序是操作系统决定的
3、父子进程的关系
(五)结束进程
1、若父进程先结束,被init进程收养,子进程变成后台进程
1)分别对父子进程加一个sleep
父子进程随机运行
子父进程ID
kill掉父进程
子进程的父进程变成了2107,也就是init进程
子进程变成后台进程
2、如果子进程先结束,父进程没有来得及回收,子进程变成僵尸进程
kill掉父进程
此时kill掉父进程,子进程消失
(五)子进程进阶
1、孙进程
子进程也进行了for循环
解决办法:子进程执行结束break
(六)进程的退出
1、exit会刷新缓冲流-库函数-<stdlib.h>
2、_exit不会刷新缓冲流-系统调用-<unistd.h>
3、main函数return会隐式调用exit
两个结果相同,其他函数return只是返回上一级
4、exit和return两个结果相同
4、_exit:没有刷新流缓冲区
(六)进程的回收
*1、wait-回收并查看子进程的情况
2、status和结束返回2的进程不同
3、16进制打印200不是2
4、用宏来取得进程的返回值
5、父进程对子进程的回收
1)不回收
2)回收
僵尸进程:子进程结束,但是没有被父进程回收
*(二)waitpid进程回收
1、waitpid
2、指定pid
3、WNOHANG不阻塞,直接执行,如果当前子进程还未结束,则会出错
出错:
修正:休眠2秒,等子进程结束
二、exec函数族
(一)exec函数族的执行过程
1、父进程的父进程是shell
2、函数族-有很多个函数组成
3、通过调用exec函数族执行某个程序
4、调用exec函数族进程当前内容被指定的程序替换
5、相当于父子进程执行不同程序
(二)execl函数和execlp函数
1、execl-需要写全部路径
相当于命令“ls -a -l”
2、execlp-不需要写全部路径
执行效果和execl相同
(三)execv函数和execvp函数
1、execv-全部路径-将命令定义成数组
2、execvp-不需要全部路径-数组
(四)system函数
1、system的实现
*(五)exec函数族特点
1、exec函数族-进程当前内容被指定内容替换,exec函数以后的程序没有被执行
2、第0个参数不使用但是要写
3、父子进程执行不同程序
三、GDB调试
(一)GDB调试多进程程序
1、想要用GDB调试,编译加-g
gcc -g -o 编译生成的名字 文件名
2、进入gdb模式命令
gdb 可执行文件
3、quit 退出
4、start开始调试
5、n进入下一步
6、默认跟踪父进程
1)set follow-
显示可以跟踪的进程
2)set follow-fork-mode
可以继续选择跟踪父子进程
3)跟踪孩子
set follow-fork-mode child
4)同时调试父子进程
调试子进程,同时可以切换父进程
5)切换调试的进程
info inferiors查看可以运行的进程
6)切换跟踪进程
inferior 序号
7、调试多进程
四、守护进程
(一)守护进程的概念
1、守护进程是后台进程,始终是后台运行
2、守护进程独立于终端
3、进程组-会话-控制终端
与控制终端连接的进程就是控制进程。
1)进程组
2)会话
一个终端界面可以理解成一个会话
3)控制终端-就是终端
4、守护进程
5、守护进程是孤儿进程
6、守护进程的创建
1)创建子进程,然后令子进程变成孤儿进程,被init收养,变成后台进程
此时子进程已经被init收养
2)创建会话-setsid
称为新的会话组长
自己当家做主人了
3)改变工作目录-不被之前的父进程目录局限
4)重设掩码-创建文件权限(不是必须的)
5)关闭打开的文件描述符
关闭文件描述符后,不会在打印到屏幕
守护进程不能在屏幕上打印东西,不能接收键盘的输入
6)nohup ./test.c &:将前台进程转为后台进程
没有写守护进程变成后台的方法
守护进程的实现
五、线程的创建和参数传递
(一)线程的基本特点
1、线程共享相同的地址空间和全局变量
2、线程是在windows下创建的,被Linux引用,Linux不分线程和进程
3、线程的特点
4、线程共享资源
常用:静态数据、文件描述符、工作目录
5、 线程的私有资源
常用:错误号、堆栈
(二)使用pthread库函数创建线程
1、pthread线程库
线程不是通过Linux内核实现,而是由线程库来实现。
2、创建线程
(三)常见编译错误及处理方法
链接错误线程编译要加上-lpthread
(四)线程的运行特点
1、线程创建需要时间,如果主进程退出,线程不能得到执行,马上退出
主进程退出,创建的线程也会退出
2、sleep 1秒,线程创建成功
(五)线程id的获取
1、线程的退出
2、pthread_exit()函数-常用于清理线程
3、打印tid
1)主函数中,直接获取
2)pthread_self()函数-函数中获得自己的id
3)打印pid
**(六)线程的参数传递
1、通过地址传递参数,注意类型转换
void定义下的arg是任意类型,直接转换成Int类型
2、也可以值传递-可能会报警,需要程序员自己保证数据长度正确
3、建立多个线程并分别向其传参
(七)段错误的原因及处理方法
六、线程的回收及内存演示
(一)pthread_join与pthread_exit
相当于进程中的wait函数
1、pthread_join主函数对线程的回收-阻塞函数,线程没有回收则等待
pthread_join在主函数中,接收线程结束的返回值
2、pthread_exit 线程结束函数
(二)pthread_join函数特点
1、一个缺点,如果是单线程回收方便,循环创建多线程,如果最开始的线程没有结束,pthread_join也会阻塞
(三)线程分离pthread_detach
1、线程主动与主控线程断开关系,自己可以回收,不需要pthread_join
2、pthread_detach使用
在主函数中:
不阻塞
在线程函数中:
3、线程属性分离
4、回收线程的三种方式
1)pthread_join(局限):阻塞,多线程阻塞
2)pthread_detach
3)创建线程时分离属性
(四)线程回收内存演示
查看内存命令: top -p 进程号
线程回收前
等待线程回收后
七、线程的取消和清理
(一)pthread_cancel与线程取消点
1、查看线程-L大写
ps -eLf |grep detach
2、杀掉线程
1)必须有取消点-阻塞
2)gdb命令:bt-打印调用栈
(二)GDB调试段错误
(三)pthread_testcancel手动设置取消点
1、线程取消需要取消点-阻塞
2、如果不确定有没有取消点,可以手动设置一个取消点
(四)pthread_setcancelstate设置取消属性
1、取消取消点
2、恢复其他地方取消点
3、主函数中的sleep
(五)线程取消pthreas_setcanceltype
(六)线程清理
1、如果线程申请了一块内存,但线程因为阻塞被取消,则会造成内存泄露
2、线程清理是在线程异常退出之后,进行清理
3、线程的清理函数本质上是宏定义,必须成对使用
push-pop
4、
return可以结束线程,但是不能出发清理函数
5、可以cancel自己
两种情况:
1)到取消点取消,取消点之前可以取消
2)立即取消
八、**互斥锁/读写锁/死锁
一、互斥锁的概念和使用
(一)临界资源
1、临界资源是只允许一个资源访问的任务,互斥资源
2、外设、磁盘都是临界资源
3、临界区是访问临界资源的代码
(二)互斥机制
1、动态初始化创建
2、静态方式创建
3、锁的销毁
4、互斥锁的使用
5、申请锁
1)两个函数的区别
6、释放锁
*(三)互斥锁
1、创建两个线程,打印字符到文件中
主函数:
1.txt:两个线程随机打印,乱序
2、使用静态方式定义互斥锁
1)初始化互斥锁
2)添加互斥锁
3)添加了互斥锁之后的1.txt
4)如果线程执行多个任务,例如多个文件的写操作,需要定义多个互斥锁
二、读写锁概念和使用
1、读写锁-提高线程的读写速度-可同时读
2、防止读的时候有线程写入
3、同一时刻只有一个线程可以获得写锁,同一时刻可以有多个线程获得读锁
4、代码
1)在主函数中定义读写锁
2)结构体变量声明
3)加锁
4)结果与互斥锁相同
结论:读写锁与互斥锁结果相近
5、读写锁-只读
读写锁比互斥锁好的地方是,读写锁可以允许写的时候读
三、死锁的避免
1、死锁的概念
2、死锁都是在一把锁的情况下
3、代码
1)设置两把锁,两个线程同时调用两个资源
2)解决办法
延长休眠时间
1、使用一把锁,锁越少越好
2、获得1,2的先后顺序相同
九、条件变量的使用及注意事项
十、线程尺及gdb调试多线程
一、线程池的概念
1、一般线程创建使用完都会被回收
2、线程的创建和销毁>线程的执行,时间不划算
3、线程池的结构
1)任务队列
2)线程池工作线程
二、线程池的实现
三、线程的GDB调试
1、主函数
2、设置断点 b
3、run运行程序
4、info thread查看线程
5、切换线程
6、下一步next
7、执行完一个线程后再次查看,线程消失
8、
9、设置线程锁
设置之后,除了选定的线程,其他不执行
10、切换到第3个,第6行断电
十一、有名管道和无名管道
一、无名管道基础
(一)进程间通信方式
**(面试题)1、进程间通信的方式
1)无名管道-亲缘进程
2)有名管道
*3)信号
4)共享内存
5)套接字-网络-进程间通信
进程-进程/主机-主机
开销最大
6)古老
(二)无名管道的特点
1、概念
相当于共享内存,通过管道传递消息
2、特点
1)只能父子或兄弟
2)单工通讯-固定读端和写端
2)创建两个文件描述符
3、无名管道的创建
单工通信-每个进程fd[0]和fd[1]只能用一个
(三)无名管道的创建
父子进程间的通信:
只有读操作之后才能打印buf
二、无名管道进阶
1、父进程创建了无名管道
2、子进程进程描述符相同
3、关闭管道
父子进程都可以关闭读写管道,无论这一段是读还是写
4、在同一个进程自己读和自己写是进行不了的
子进程之间相互通信:
5、管道可以用于大于两个进程的共享
6、可以两个子进程读/写,父进程写/读
7、无名管道的读写特性
管道大小是64K
三、有名管道概念和使用
(一)有名管道的特点
1、通过文件系统,但是文件不是放在磁盘,放在内存中,通过文件IO
2、有名管道的特点
(二)有名管道的创建
1、有名管道的创建
路径不能是Linux和wins共享目录
2、直接创建在根目录下
(三)有名管道的读写
1、写进程代码:
2、读进程代码:
读写结果:
四、有名管道的注意事项
1、不能使用读写方式打开文件
2、文件可以创建在根目录下,但是不能创建在共享目录下
3、默认情况下只写O_RDWR和只读O_RDONLY参数是阻塞的,加了第二个参数,不会阻塞,没有内容可读,直接退出
一个参数阻塞:
两个参数:
读文件:没有内容直接报错
有内容读内容
写文件:打开失败
注意事项
1、BSS存放没有初始化的全局变量。
2、static声明的变量不在栈中,和全局变量放在一起。
3、栈:参数,返回值,局部变量
堆:malloc
4、ps -elf 查看Linux下进程的详细信息
ps -elf|grep PID查看某个进程
top动态查看Linux下进程的实时信息
top -p PID查看某个进程
/proc查看进程的某个目录
5、子进程和父进程执行的代码相同,但子进程只执行程序中fork函数之后的代码(也就是子进程创建后的代码)。
6、fork函数创建子进程成功后,父进程返回子进程的进程号(进程号大于0),子进程返回0。
7、若父进程先结束,子进程变成孤儿进程,被init进程收养变成孤儿进程;若子进程先结束,父进程没有及时回收,子进程变为僵尸进程。
8、子进程通过exit/_exit/return返回,父进程通过wait(&status)回收子进程,wait有返回值。
9、exec函数使得进程当前内容被指定内容替换,exec函数以后的程序没有被执行自己,子进程调用exec函数族,父进程不受影响。
10、gdb调试命令:gcc -g -o 编译生成的名字 文件名
11、关闭文件描述符1,2,3,守护进程不能在屏幕上打印东西,不能接收键盘的输入。
12、内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数,打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。文件描述符0, 1, 2 默认为 stdin stdout stderr。
13、pthread_join在主函数中,接收线程结束的返回值。pthread_exit 线程结束函数,在线程内,返回值可以是一句提示语句。
14、