1.进程
1.1 什么是进程?
进程是程序的一次动态执行过程,包括创建、调度、消亡。
1.1.1如何查看进程
在Linux下查看进程有以下几种方法:
- ps aux
- ps -ef
- top
- pstree
- ps axj
1.1.2进程和程序的区别
程序(a.out)是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念。
进程(./a.out)是一个动态的概念,它是程序执行的过程,包括创建、调度和消亡。
1.1.3进程是程序执行和资源(内存)管理的最小单位
为什么说进程是程序执行和资源管理的最小单位?
因为每一个进程都有一个0~4G的虚拟内存
为什么要有虚拟内存?
多任务、安全
1.1.4进程的类型
(1)交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。
(2)批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。
(3)守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。
1.1.5进程运行状态
(1)运行态:此时进程或者正在运行,或者准备运行。
(2)等待态:此时进程在等待一个事件的发生或某种系统资源。
可中断
不可中断
(3)停止态:此时进程被中止。
(4)僵死态:这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构体。
为了更好的管理Linux所访问的资源,系统在内核头文件include/linux/sched.h定义了进程控制块(PCB)结构体task_struct来管理每个进程的资源
内核空间进程资源即PCB相关的信息,包括进程控制块本身,打开的文件表项、当前目录、当前终端信息、线程基本信息、可以访问内存地址空间、PID、PPID、UID、EUID等,也就是说,内核通过PCB结构体可以访问到进程的所有资源信息
(5)死亡态
进程状态图:
1.1.6进程的模式
1.2进程相关的系统调用
1.2.1创建进程:fork
注意:fork之后,父进程和子进程存在资源竞争的关系,谁先调度由调度算法来决定。
1.2.2获取进程的进程号和父进程号:getpid,getppid
1.2.3进程的退出
exit、 _exit 、return(主函数的return)
需要注意的是exit()会刷新缓冲区,而_exit()不会刷新缓冲区!
1.2.4孤儿进程
父进程先于子进程退出,子进程会被systemd进程收养,子进程会变成后台进程。
1.2.5僵尸进程
子进程先退出,父进程没有及时回收子进程的资源(PCB结构体)(没有查看子进程的退出状态),此时就成为了僵尸进程。
注意:父进程不退出,子进程会一直保持僵死状态,直到父进程退出,被systemd回收。
1.2.6防止僵尸进程的出现:wait(),waitpid()
子进程退出时父进程及时回收子进程的资源,可以调用wait或者waitpid。
1.2.6.1wait
1.2.6.2waitpid
1.2.7exec函数族
l:list 以列表的形式传参
v:vector 数组
p:path 系统会自动从环境变量“$PATH”所包含的路径中进行查找
1.3守护进程
1.3.1守护进程的特点
- 从系统启动开始运行,系统关闭时停止运行
- 后台进程,与终端无关
1.3.2实现守护进程:
(1)不能和终端具有亲缘关系
(2)与终端无关,也就是要把控制终端变成?
1.3.3进程组和会话组
1.3.4守护进程的创建步骤
(1)创建子进程,父进程退出。 备注:从形式已经脱离了终端。
(2)在子进程中创建新会话。 控住终端变成 “ ?”。
pid_t setsid();
(3)改变当前目录为根目录 防止用户误删。
int chdir();
(4)重设文件权限掩码 提高代码的灵活性 , 安全
mode_t umask();
(5)关闭文件描述符
1.4通信相关概念
1.4.1通信的模式
单工:A-->B (键盘输入)
半双工:A--->B 或者 B--->A (对讲机)
双工:A--->B 同时 B-->A(打电话)
同步:
异步:
1.4.2通信方式
1.5管道
1.5.1无名管道
1.5.1.1相关概念
1.5.1.2创建无名管道
为什么无名管道只能用于具有亲缘关系间的进程的通信?
无名管道没有名字,不同的进程无法获取同一个文件描述符,具有亲缘关系的进程之间可以继承,从而得到同一个文件描述符。
注意:
(1)当管道中无数据时,读操作会阻塞
(2)向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。
(3)只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)-->管道破裂
1.5.1.3测试管道破裂
1.5.2有名管道
1.5.2.1相关概念
1.5.2.2创建有名管道
1.6信号
1.6.1相关概念
1.6.2信号安装or信号注册函数
1.6.2.1忽略信号
1.6.2.2采用默认操作
1.6.2.3自定义信号处理函数
1.6.3闹钟函数:alarm
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号。
1.6.4进程挂起:pause
pause()函数是用于将调用进程挂起直到收到信号为止。
1.6.5发送信号给进程或进程组:kill
1.6.6进程向自己发送信号:raise
1.7共享内存
1.7.1相关概念
SystemV提供的IPC机制主要由消息队列、共享内存、信号量3种机制。和文件一样,IPC在使用前必须先创建,每种IPC都有特定的生产者、所有者和访问权限。使用ipcs命令查看当前系统正在使用的IPC工具。
1.7.2共享内存的创建步骤
(1)创建/打开共享内存
(2)映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
(3)撤销共享内存映射
(4)删除共享内存对象
1.7.3只用信号实现同步
写端:
读端:
1.8有名信号量
有名信号量:进程间的同步
无名信号量:线程间的同步
1.8.1创建有名信号量
1.8.1.1sem_open
1.8.1.2P操作和V操作
sem_wait( ):P操作 -1
sem_wait在信号量大于0时,它将这个信号量的值减1,若信号量的值为0,sem_wait将会阻塞
sem_post( ):V操作 +1
sem_post相当与V操作,它将信号量的值加1,同时唤醒等待的进程
1.8.1.3sem_close
1.9消息队列
1.9.1相关概念
注意:消息类型不能为0!
1.9.2消息队列的创建步骤
发送端:
1.创建并打开消息队列
2.添加消息
3.接受消息
4.删除消息队列
2.线程
2.1线程的相关概念
2.2创建线程:pthread_create
注意:线程之间也存在资源竞争的关系!
2.3线程的等待函数:pthread_join
2.4将子线程设置为游离态:pthread_detach
2.5线程的退出函数:pthread_exit
3.多线程
3.1同步
3.1.1初始化:sem_init
3.1.2P/V操作:sem_wait,sem_post
sem_wait( ):P操作 -1
sem_wait在信号量大于0时,它将这个信号量的值减1,若信号量的值为0,sem_wait将会阻塞
sem_post( ):V操作 +1
sem_post相当与V操作,它将信号量的值加1,同时唤醒等待的进程
注意:也可以保护临界资源的完整性,而且实现同步
3.2互斥
3.2.1步骤
- 创建锁:pthread_mutex_t
- 初始化锁:pthread_mutex_init
- 上锁:pthread_mutex_lock
- 解锁:pthread_mutex_unlock
- 销毁锁:pthread_mutex_destroy