1.进程
程序:二进制文件,占用的磁盘空间 进程: 启动的程序 所有的数据都在内存中 需要占用更多的系统资源 cpu,物理内存
2.并行和并发
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是 在同一个处理机上运行
并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几 个时间区间之间来回切换,由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应 用程序同时在进行。如:打游戏和听音乐两件事情在同一个时间段内都是在同一台电脑上完成了从开始到结 束的动作。那么,就可以说听音乐和打游戏是并发的
并行:当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程 互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行
并行和并发的区别: 并发,指的是多个事情,在同一时间段内同时发生了。 并行,指的是多个事情,在同一时间点上同时发生了。 并发的多个任务之间是互相抢占资源的。 并行的多个任务之间是不互相抢占资源的、 只有在多CPU或者一个CPU多核的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行
3.PCB(进程控制块)
每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,linux内核的进程控制块是task_struct结构 体。 /user/src/linux-headers-3.16.0-30/include/linux/sched.h头文件中可以查看struct task_struct结构体定义。内部 成员有很多,我们重点掌握以下部分即可。
进程id。系统中每个进程有唯一的id,在C语言中用pit_t类型表示,其实就是一个非负整数 进程的状态,有就绪,运行,挂起,停止等状态。进程切换时需要保存和恢复一些CPU寄存器 描述虚拟地址空间的信息 描述控制终端的信息 当前工作目录(Cueernt Working Directory) umask掩码 文件描述符,包含很多指向file结构体的指针 和信号相关的信息 用户id和组id,stat 会话(Session)和进程组 进程可以适用的资源上线(Resource Limit),umilit -a
4.进程状态
进程基本的状态有五种,分别为初始态,就绪态,运行态,挂起态和终止态。 其中初始态为进程准备阶段,常常与就绪态结合来看。
2.进程控制
1.fork函数
包含的头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t fork(void);
两个返回值:
=0:当前进程为子进程
>0:当前进程为父进程
‐1,出错
一个进程,包括代码、数据和分配给进程的资源。 fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的 事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进 程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
用户数据一样,进程ID不一致。 1.fork函数的返回值 2.子进程创建成功之后,子进程的执行位置 3.父子进程的执行顺序 不一定 4.如何区分父子进程
2.getpid/getppid函数
getpid:得到当前进程的PID getppid:得到当前进程的父进程的PID
3.ps和kill命令
ps 查看进程信息 部分参数: a : 显示现行终端机下的所有程序,包括其他用户的程序 u: 以用户为主的格式来显示程序状况 x: 显示所有程序,不以终端机来区分
kill 向指定的进程发送信号 kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或job指令查看。 参数: -a:当处理当前进程时,不限制命令名和进程号的对应关系; -l :若不加选项,则-l参数会列出全部的信息名称; -p:指定kill 命令只打印相关进程的进程号,而不发送任何信号; -s :指定要送出的信息; -u:指定用户。
4.父子进程间的数据共享
fork之后两个地址空间区数据完全相同
各个进程的地址空间中的数据是完全独立的 对于同一个变量,读时共享 写的时候分别在物理地址上拷贝一份变量进行单独读写
父子进程之间可不可以通过全局变量通信? 不能,两个进程内存不能共享
5.exec函数族
6.孤儿进程和僵尸进程
孤儿进程: 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进 程(进程号为1)所收养,并由init进程对它们完成状态收集工作 为了释放子进程的占用的系统资源: 进程结束之后,能够释放用户区空间 释放不了PCB,必须由父进程释放 僵尸进程: 一个比较特殊的状态,当进程退出父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产 生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。 是一个已经死掉了的进程
僵尸进程
7.进程回收
wait 阻塞函数 函数作用: 1.阻塞并等待子进程退出 2.回收子进程残留资源 3.获取子进程结束状态(退出原因)
pid_t wait(int *wstatus);
返回值:
‐1 : 回收失败,已经没有子进程了
>0 : 回收子进程对应的pid
参数 :
status判断子进程如何退出状态
1.WIFEXITED(status):为非0 ,进程正常结束
WEXITSTATUS(status)
如上宏为真,使用此宏,获取进程退出状态的参数
2.WIFSIGNALED(status):为非0,进程异常退出
WTERMSIG(status):
如上宏为真,使用此宏,取得使进程种植的那个信号的编号
调用一次只能回收一个子进程
waitpid函数 函数作用:同wait函数
pid_t waitpid(pid_t pid, int *status, int options);
参数
1.pid: 指定回收某个子进程
pid == ‐1 回收所有子进程
while( (wpid=waitpid(‐1,status,0)) != ‐1)
pid > 0 回收某个pid相等的子进程
pid == 0 回收当前进程组的任一子进程
pid < 0 子进程的PID取反(加减号)
2.status: 子进程的退出状态,用法同wait函数
3.options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞
返回值:
>0 :返回清理掉的子进程ID
‐1 :回收失败,无子进程
如果为非阻塞
=0 :参数3为WNOHANG,且子进程正在运行
8.vfork创建进程 vfork也可以创建进程,与fork有什么区别呢? 区别一:vfork可以直接使用父进程存储空间,不拷贝 区别二:vfork可以保证子进程先运行,当子进程调用exit退出后,父进程才执行
9.进程退出
1.正常退出 1.main函数调用return 2.进程调用exit(),标准C库 3.进程调用 _exit() 或者 _Exit(),属于系统调用 补充: 4.进程最后一个线程返回 5.最后一个线程调用pthread_exit
2.异常退出 1.调用abort函数 2.当进程收到某些信号时,比如ctrl +C 3.最后一个线程对取消(cancellation)请求做出相应
不管进程如何终止,最后都会执行内核中的同一段代码,这段代码和相应进程关闭所有打开描述符,释放它所使用 的存储器等。 对上述任一一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit, _exit 和 _ Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数,在异常终止情况下,内核(不是 进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进 程都能用wait或waitpid函数取得终止状态。
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);