- 进程(process)
1.进程相关的概念
程序:用计算机语言对算法的具体实现,它是静态的
进程(process):
- .正在运行的程序.它是动态的
- .资源管理的最小单位
- 虚拟内存空间又分:
内核空间:1G(0xC000 0000 - 0xFFFF FFFF)
用户空间:3G 包括: 堆、栈 、代码段 、数据段 、不可访问空间:0 ~ 0x0804 8000(128M)
当我们的进程结束之后,用户空间会随着进程的结束而释放,但内核空间不会,这1G空间需要创建它的父进程来回收,否则会称为僵尸进程(Bug)
任务:
两个互不相关的任务:
- 进程分类:
- 交互进程:
shell命令进程,文本编辑器(vim),图形应用程序等
既有前台进程,又有后台进程
可以将后台运行的程序,切换到前台来运行
fg 作业号
- 批处理进程
gcc(预处理,汇编,编译,链接) gcc 1.c -o 1 (四步合四为1)
数据库搜索引擎
- 守护进程
这类进程一直在后台运行,和任何终端都不关联,很多系统进程(各种服务)都是以守护进程的形式存在
3.查看进程
1.windows进程 ctrl+alt+Del
2.Linux下查看进程
ps -ef:查看所有进程及其PID(进程号),系统时间,命令详细目录,执行者等
ps aux :显示不了状态,
top:动态显示系统中运行的程序(一般每隔5s刷新一次)
pstree:以树形结构显示系统当中的进程
kill:输出特定的信号给指定PID(进程号)的进程‘
kill -l列出所有可用的信号名称.
4.进程相关的特性
- 进程间状态转换图
双向链表队列:先入先出
2.进程间的状态
1.就绪态
(1)就绪状态:进程已获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
2.运行态 (R) TASK_RUNNING
(2)执行状态:进程已获CPU,正在执行。单处理机系统中,处于执行状态的进程只一个;多处理机系统中,有多个处于执行状态的进程。
3.等待态 (阻塞态)
(3)阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)
(1).可中断的等待态 S (标准输入,sleep()) (TASK_INTERRRUPTIBLE)
(2).不可中断的等待态 D (TASK_UNINTERRUPTIBLE)
4.停止态 (T) TASK_STOPPED
5.死亡态 (X) EXIT_DEAD
6.僵尸态 (Z) EXIT_ZOMIE
3.进程的优先级
进程的优先级范围:-20到19,数字越大,优先级越低
nice:以某种优先级执行我们的程序
renice :可以修改正在运行程序的优先级
5.进程相关的函数
1.进程创建 fork vfork()
1.方法一:系统调用程序 创建进程 单进程
2.方法二:程序自己创建进程 fork()
好处:可以同时处理多项工作,互不干扰
缺点:进程是资源分配的最小单元
每创建一个进程,虚拟出来4G内存空间,子进程几乎复制了父进程所有的资源
3.方法三:程序自己创建子进程vfork()
由于fork完整地拷贝了父进程的整个地址空间,因此执行速度是比较慢的。为了提高效率,Unix系统设计者创建了vfork。vfork也创建新进程,但不产生父进程的副本, 它通过允许父子进程可访问相同物理内存从而伪装了对进程地址空间的真实拷贝,当子进程需要改变内存中数据时才拷贝父进程。这就是著名的“写操作时拷贝”(copy-on-write)技术
3.fork和vfork区别
1.数据段不同
fork(): 子进程拷贝父进程的数据段(堆,栈等),代码段.
vfork():子进程与父进程共享数据段.
2.执行次序
fork(): 父子进程的执行次序不确定.
vfork():保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。
- 进程执行
1.父子执行顺序:不确定
2.若父进程先结束,子进程未结束,子进程就会称为孤儿进程
3.若子进程先结束,父进程未结束,但父进程没有回收子进程的内核资源,子进程会称为僵尸进程
- 进程主动退出 exit _exit
exit()
_exit()
exit()和_exit()
(1).exit()函数叫C语言库提供,库函数._exit()函数叫系统调用函数,Linux系统提供的
(2).exit()自带清理缓冲区,_exit()不清理缓冲区
- 如何避免僵尸进程的产生(waitpid()回收子进程资源)
wait() 阻塞回收
waitpid(); 非阻塞回收
不接受子进程退出的状态
wait(NULL); 阻塞等待任意一个子进程退出,不接受子进程退出的状态
waitpid(-1,NULL,0) 《====》wait(NULL)
接受子进程退出的状态 (宏使用)
6.exec()函数族:
1.为什么要使用Exec函数族
(1)fork()子进程是为了执行新的程序
(2)可以直接在子进程if中写入新程序的代码,但是不够灵活,因为只能将子进程程序的源代码贴过来执行(必须知道源码,而且源码太长不好控制)比如要执行 ls -la命令就不行。
(3)使用exec运行新的可执行程序(可以把一个编译好的的可执行程序直接加载运行)
(4)我们有了exec族函数后,我们典型的父子进程就是这样的:子进程需要运行的程序被单独编写、单独编译成一个可执行文件(叫hello),项目是一个多进程项目,主程序叫父进程,fork创建了一个子进程后在进程中exec来执行hello,达到父子进程分别作不同的程序(宏观上)同时运行。
2.如何使用exec函数
exec函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。在执行完之后,原调用进程的内容除了进程号外,其他全部都被替换了。
如果某个进程想同时执行另一个程序,它就可以调用fork函数创建子进程,然后在子进程中调用任何一个exec函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样
7.守护进程
1.什么是守护进程
1.它是守护进程,也就是通常所说的Daemon进程,是Linux三种进程之一,通常在系统启动时运行,在系统关闭时结束.
2.始终运行在后台,后台服务进程
3.独立于任何终端(和终端无关)。
4.周期性地执行某种任务或等待处理特定事件。
2.什么会话
Linux是以会话(session),进程组的方式管理进程.每个进程属于一个进程组,子进程同属于该进程组.
会话是由一个或者多个进程组的集合,通常用户打开一个终端,系统就会创建一个会话,所有通过该终端运行都属于这个会话,shell进程--->会话组的组长,一个会话最多打开一个控制终端,当控制终端结束时,所有的进程也跟着结束。
3.守护进程的创建步骤
1.创建子进程,父进程退出. fork() 为了成为叫后台进程
2.创建新的会话 (脱离原来的会话,成为新的会话组长)
3.修改当前目录
4.修改文件掩码
5.关闭从父进程继承下来的文件描述符
2.线程(thread)
1.为什么学习线程
(1).由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大
(2).为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程
(3).在同一个进程中创建的线程共享该进程的地址空间(除了栈区之外)
2.线程的相关函数
1.线程的创建pthread_create
2.等待线程结束
pthread_join()
(1).pthread_join函数在线程退出时,用来清理线程资源。
(2).phtread_join函数会阻塞调用方,直至pthread_join所指的线程退出。
pthread_detach()
因为线程默认的状态是结合态的,所以,可以通过pthread_detach函数来设置线程为分离态。
pthread_detach函数的特点
使用pthread_detach函数后,使线程处于分离态;
使用pthread_detach函数后,线程在退出后,会自己清理资源
相较pthread_join,使用pthread_detach函数不会阻塞主线程,但是无法获取线程的返回值。
3.线程的退出:
1.pthread_exit()主动退出
2.pthread_cancel()被动退出
3.线程间数据传递
线程中除了栈区的数据其他都是共享的
- 证明全局变量,堆区数据是共享的
2.主线程给子线程传值
- 子线程给主线程传值
4.线程出现的问题:
1.互斥:
1.什么是互斥
1.多线程访问临界资源时,没有顺序而言,
若线程A访问临界资源,不允许其他线程访问资源。
2.引入互斥锁:用来保证共享数据操作的完整性。
售票系统:
每个售票窗口:类似于一个线程
20个售票窗口
pc-->12306网站
移动端--->12306 app
西安--->成都 4月29日 G456 16车5F
2互斥锁
- 定义互斥锁 (全局)
pthread_mutex_t myMutex;
2.初始化锁
3.上锁
4.解锁
- 销毁锁
2.同步:
1.什么是同步
多线程访问临界资源,按照某种顺序执行
无名信号量:用于线程间的同步
有名信号量:用于进程间的同步
信号灯集
- 无名信号量
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问.
当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现他们之间的顺序执行
- 定义全局信号量
- 初始化信号量
3.P操作 sem_wait()
当信号量值>0 会将信号量的值减1
当信号量值==0 阻塞线程
4.V操作 sem_post()
将信号量的值加1,同时唤醒等待的线程
- 生产者和消费者
3.进程间通信方式
- 传统的unix通信方式
- 无名管道
1.无名管道的特点:
(1).具有亲缘关系(父子关系,兄弟关系)间的进程间通信
(2).管道也可以看成是一个特殊的文件,对于它的读写也可以使用普通的read(),wirte()等函数,
它不属于任何文件系统,存在内核当中.
(3).它是一个半双工的通信模式,具有固定的读端(fd[0])和写端(fd[1])。
2.创建无名管道
3.实现父子进程间的通信:父:收 子:发
4.验证无名管道有多大
5.无名管道需要注意的问题
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)
- 有名管道
1.有名管道的特点:
1.适用于两个不相关的进程间的通信,是一个半双工的通信
2.有名管道可以通过路径名来指出,并且在文件系统中可见,两个进程可以把它当成普通文件进行操作,open,read,write,close
3.有名管道必须遵循先进先出的原则,不支持lseek()函数
2.创建有名管道
- 信号
1.信号的特点
是唯一一种异步通信方式
2.信号产生
1.硬件产生:
Ctrl+c SIGINT 终止前台进程
Ctrl+/ SIGQUIT 终止进程
Ctrl+z SIGTSTP 停止进程
2.软件产生
raise()函数: raise函数允许进程向自己发送信号
kill()函数 : kill函数同读者熟知的kill系统命令一样,可以发送信号给进程或进程组
编写一子进程:
子进程:
i%13==0 子进程给自己发送的信号停止信号
父进程: i++
i%19==0 父进程给子进程发送继续信号
3.内核发送
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALRM信号。
pause()函数是用于将调用进程挂起直到收到信号为止。
3.进程响应信号 signal() sigaction()
1.执行默认操作。
2.忽略信号
对信号不做任何操作,有两个信号不能忽略 SIGKILL(9) SIGSTOP(19)
3.捕捉信号
定义信号处理函数,当信号发生时,执行信号处理函数
- SystemV IPC(进程间通信方式)
SyS
- SystemV IPC基础
1.如何查看系统正在使用的IPC工具
2.ID值和Key值
1.消息队列
1.消息队列特点
1.消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
2.消息队列可以按照类型来发送/接收消息,相同的类型满足先进先出,不同的类型可以随意存取.
3.全双工通信方式
- 消息队列的相关操作
1.创建消息队列
- 按照类型添加队列
3.按照类型读取队列
2.共享内存
1.共享内存的特点
- 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
- 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
优点:
进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
缺点:
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
2.共享内存的相关操作
1.创建/打开共享内存
2.映射共享内存,
即把指定的共享内存映射到进程的地址空间用于访问
- 撤销共享内存
3.信号量一般和共享内存配合使用
1.无名信号量:适用于线程同步
sem_t
sem_init(); 初始化信号量
sem_wait(); P操作 -1
sem_post(); V操作 +1
sem_getvalue() 获得信号的值
2.有名信号量: 适用于进程同步
1创建有名信号量
2.P操作 sem_wait() -1
3.V操作 sem_post() +1
4.关闭有名信号量 sem_close()