- 进程
1.什么是进程
Windows: ctrl+alt+Del
程序:
静态的,存储在磁盘上的指令的有序集合
进程:
正在运行的程序,动态的,创建进程,进程运行,进程消亡.进程是资源分配的最小单位
Linux为了更好的管理进程,每个进程都有独立的资源空间 (4G ---->虚拟资源空间),
struct task_struct 来管理每个进程的资源.
资源空间分为两部分:(1).内核空间资源和用户空间资源
内核空间资源:进程控制块PCB,内核通过进程控制块访问进程,进程的ID号,父进程的ID号等,这部分资源在进程退出时,必须由另一个进程(父进程)来回收该资源,否则该进程成为僵尸进程
用户空间资源:当进程退出时,该资源会自动释放,堆区,栈区,数据段,代码段等
- 如何查看Linux进程
- 进程分类
- 交互进程
既可以在前台运行,也可以在后台运行
2.批处理进程(一般不接触),gcc编译器
3.守护进程
4.进程状态
1.运行态/就绪态: 进程正在运行,或者准备运行 R running or runnable (on run queue)
2.等待态(阻塞态): 进程在等待一个事件的发生或某种系统资源
可中断的等待态 S
不可中断的等待态 D
3.停止态 进程被中断 T
4.死亡态 X (僵尸态Z) 已终止进程,但内核空间资源没有被回收,僵尸态
三种状态: 就绪态 运行态 阻塞态
五种状态: 运行态 可中断等待态不可中断等待态 停止态 死亡态
七种状态: 运行态 就绪态 可中断等待态不可中断等待态 停止态 僵尸态死亡态
优先级的范围是-20到19,值越大,优先级越低,普通用户最多只能设置为0
renice 改变正在运行进程的优先级
nice 指定进程按照某种优先级进行运行
5.进程相关函数
1.进程的创建
1.fork()创建
子进程几乎复制了父进程所有的资源,如栈区,堆区,数据段,
2.vfork()
由于fork完整地拷贝了父进程的整个地址空间,因此执行速度是比较慢的。为了提高效率,Unix系统设计者创建了vfork。vfork也创建新进程,但不产生父进程的副本, 它通过允许父子进程可访问相同物理内存从而伪装了对进程地址空间的真实拷贝,当子进程需要改变内存中数据时才拷贝父进程。这就是著名的“写操作时拷贝”(copy-on-write)技术
3.fork和vfork区别
1.数据段不同
vfork():子进程与父进程共享数据段.
2.执行次序
fork(): 父子进程的执行次序不确定.
vfork():保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。
- 进程退出
3.父子进程:
(1).父子进程执行顺序不确定,看内核的调度,
(2).fork()创建子进程,子进程几乎继承父进程所有的资源,如栈区,堆区,数据段,打开的文件描述符各自都是各自的
(3).父进程先退出,子进程未结束,子进程会为称为孤儿进程.被其他进程(2081)收养
(4).子进程先退出,父进程未结束,父进程未回收子进程的资源,子进程会成为僵尸进程
4.回收进程资源(避免僵尸进程的产生)
1.wait() 阻塞回收资源
调用该函数使进程阻塞,直到任意一个子进程结束或者是该进程接收到了一个信号为止。如果该进程没有子进程或者其子进程已经结束,wait函数会立即返回。
2.waitpid() 阻塞/非阻塞
功能和wait函数类似。可以指定等待某个子进程结束以及等待的方式(阻塞或非阻塞)
参数1:pid=-1 表示等待任意子进程退出
参数2:NULL 忽略子进程退出的状态
获得子进程退出的状态int *
参数3:0 表示阻塞父进程
WNOHANG 表示非阻塞回收子进程,但必须轮询
返回值: 0 表示没有子进程结束
>0 表示结束的子进程的进程号
waitpid(-1,NULL,0) 《=====》 wait(NULL) 阻塞等待子进程退出
waitpid(-1,NULL,WNOHANG) 非阻塞等待子进程结束
5.Exec函数族
- 为什么要使用Exec函数族
(1)fork()子进程是为了执行新的程序
(2)可以直接在子进程if中写入新程序的代码,但是不够灵活,因为只能将子进程程序的源代码贴过来执行(必须知道源码,而且源码太长不好控制)比如要执行 ls -la命令就不行。
(3)使用exec运行新的可执行程序(可以把一个编译好的的可执行程序直接加载运行)
(4)我们有了exec族函数后,我们典型的父子进程就是这样的:子进程需要运行的程序被单独编写、单独编译成一个可执行文件(叫hello),项目是一个多进程项目,主程序叫父进程,fork创建了一个子进程后在进程中exec来执行hello,达到父子进程分别作不同的程序(宏观上)同时运行。
2.如何使用exec函数
exec函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。在执行完之后,原调用进程的内容除了进程号外,其他全部都被替换了。
如果某个进程想同时执行另一个程序,它就可以调用fork函数创建子进程,然后在子进程中调用任何一个exec函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样
6.守护进程
1.什么是守护进程
1.它是守护进程,也就是通常所说的Daemon进程,是Linux三种进程之一,通常在系统启动时运行,在系统关闭时结束.
2.始终运行在后台,后台服务进程
3.独立于任何终端(和终端无关)。
4.周期性地执行某种任务或等待处理特定事件。
2.什么会话
Linux是以会话(session),进程组的方式管理进程.每个进程属于一个进程组,子进程同属于该进程组.
会话是由一个或者多个进程组的集合,通常用户打开一个终端,系统就会创建一个会话,所有通过该终端运行都属于这个会话,shell进程--->会话组的组长,一个会话最多打开一个控制终端,当控制终端结束时,所有的进程也跟着结束。
3.创建守护进程
1.创建子进程,父进程退出 (让子进程称为后台进程)
if(fork()>0)
{
exit(0)
}
- 子进程创建新会话(脱离原来的会话,称为新会话的组长)
- 更改当前目录
守护进程一直在后台运行,其工作不能被删除(避免别人删除你的目录)
chdir(“/”); ---->只有root能够运行
chdir(“/tmp”) ---->所有用户都能执行,删除只有root用户才能删除
4.修改文件掩码
只修改该守护进程创建的文件
5.关闭从父进程继承下来所有文件描述符
for(i=0;i<getdtablesize();i++)
{
close(i);
}
6.周期性重复性干一件,将系统时间每个5秒写入到一个文件中
2.线程:
1.什么是线程
线程往往称为叫轻量级的进程
2.什么时候用多进程,什么时候用多线程
1)创建和销毁较频繁使用线程,因为创建进程花销大。
2)需要大量数据传送使用线程,因为多线程切换速度快,不需要跨越进程边界。
3)安全稳定选进程;快速频繁选线程;
3.线程相关函数
1.创建线程
2.回收子线程资源
1.阻塞回收子线程资源pthread_join()
2.非阻塞回收子线程资源pthread_detach()
pthread_detach();设置主线程和子线程为分离态,不阻塞,子线程结束时,自己帮自己回收资源。
3.线程的退出
1.主动退出
2.被动退出
- 线程间通信
1.验证全局变量是共享的
2.验证堆区在各个线程中也是共享的
栈区的数据是不共享的
- 主线程给子线程传值
- 子线程给主线程传值
pthread_exit()和pthread_join()配合使用,子线程给主线程传值
5.线程遇到的问题
1.互斥
1.什么是互斥
1.多线程访问临界资源时,没有顺序而言,
若线程A访问临界资源,不允许其他线程访问资源。
2.引入互斥锁:用来保证共享数据操作的完整性。
2.互斥锁的使用
买火车票问题:
售票窗口
Pc 12306网站
移动端 12306 APP
西安--->上海 1月15号早上8:30 G436 15车6F座
- 定义全局锁
pthread_mutex_t myMutex;
- 初始化锁
- 上锁
- 解锁
销毁锁
2.同步
1.什么同步
多线程访问临界资源,按照某种顺序执行
2.信号量
1.无名信号量:主要用于线程间通信
2.有名信号量:主要用于进程间通信
3.信号灯集
POSIX:可移植操作系统接口
无名信号量:本质上一个非负的整数计数器。
- 初始化信号量
- 信号量P操作 (信号量>0 -1 信号量=0 就会阻塞线程)
- 信号量V操作 (信号量的值加1,同时唤醒等待的线程)
- sem_getvalue()用于获取信号量的值
3.进程间通信
UNIX平台进程通信方式
早期进程间通信方式
AT&T的贝尔实验室,对Unix早期的进程间通信进行了改进和扩充,形成了“system V IPC”,其通信进程主要局限在单个计算机内
BSD(加州大学伯克利分校的伯克利软件发布中心),跳过了该限制,形成了基于套接字(socket)的进程间通信机制
Linux继承了上述所有的通信方式
- 传统的Unix通信方式
- 无名管道
1.无名管道特点
1.只能用于具有亲缘关系(父子进程/兄弟进程)的进程之间的通信
2.半双工的通信模式,具有固定的读端(fd[0])和写端(fd[1])
3.管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write,close函数,在文件系统中不可见的,数据保存在内核中。
- 无名管道相关函数
管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。
- 测试无名管道有多大
4.无名管道遇到的问题
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)
- 有名管道
1.有名管道的特点
有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见,数据还是在内核中。
半双工的通信,进程通过文件IO来操作有名管道,open,write,read,close
有名管道遵循先进先出规则
不支持如lseek() 操作
2.有名管道的相关函数
- 作业:
多进程实现全双工:
F1 创建子进程:
父进程:负责发
子进程:负责收
F2 创建子进程
父进程:负责收
子进程:负责发
多线程实现全双工
F1: 创建两个子线程
子线程1:负责发
子线程2:负责收
F2: 创建两个子线程
子线程1:负责收
子线程2:负责发
3.信号
1.信号特点:
唯一异步通信方式,在软件层次上对中断机制的一种模拟
2.信号产生方式:
1.硬件产生:
Ctrl+c ------SIGINT 终止进程
Ctrl+\ ------SIGQUIT 终止进程
Ctrl+z ------SIGTSTP 停止进程
2.软件产生
raise():自己给自己发送信号
kill():给别人发送信号
3.内核产生:
alarm()
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALRM信号。
pause()函数是用于将调用进程挂起直到收到信号为止。
3.捕获信号
#include <signal.h>
typedef void (*sighandler_t)(int);
参数1:接受的信号
参数2:接受到信号进行处理
(1).默认处理
SIG_DFL,
(2).忽略处理 注意:SIGKILL (9) SIGSTOP (19)这两个信号无法忽略
SIG_IGN,
sighandler_t signal(int signum, sighandler_t handler);
(3).当接收到信号之后,执行信号处理函数 void (int )
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
2.SystemV通信方式
1.systemVIPC基础
2.key值和ID值
3.消息队列
1.消息队列特点:
消息队列由消息队列ID来唯一标识,双全工的通信方式
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列可以按照类型来发送/接收消息,先进先出.
2.消息队列的相关函数
1.创建消息队列
2.添加消息
3.读取消息
4.控制消息队列msgctl
3.作业:
多线程:
线程1:发送消息类型为100
线程2: 接受消息类型为200
- 共享内存
1.共享内存特点
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
2.共享内存相关函数
1.创建共享内存shmget
2.映射共享内存 shmat
3.撤销共享内存映射shmdt
3.共享内存同步
5.信号量(有名信号量和共享内存配合使用)
无名信号量:线程间的同步:
sem_init() 信号量的值
sem_wait() P操作 >0 -1 ==0 阻塞进程
sem_port() V操作 +1 同时唤醒进程
有名信号量:进程间的同步:
sem_wait() P操作 >0 -1 ==0 阻塞进程
sem_port() V操作 +1 同时唤醒进程
sem_open() 创建有名信号量
信号灯集: semget