程序执行方式:
1.顺序执行 :一个程序执行完,才能执行下一个程序。cpu利用率低
2.并发执行 : 一个程序执行有很多步骤,取值 -> 执行 ->写回
不同的步骤需要不同的硬件完成,使用并发就可以实现多个程序同时执行,这里就引入了‘进程’这个概念,提高cpu的利用率
进程和程序的区别 :
(1) 程序是静态概念(是指令与数据的有序集合,“程序文件”)
进程是动态的概念,进程是有声明周期的(动态产生,动态消亡)
(2) 进程是一个程序的一个执行活动。
一个程序可以对应多个进程。
(3) 进程是独立的活动单位;
进程是竞争资源的基本单位。
进程的状态
就绪态(Ready):准备工作已经做好了,只要有CPU,就可以工作。
运行态(Running):cpu正在执行这个进程的指令
阻塞态(Blocking或waiting):进程正在等待外部事件的发生。
creat产生进程 -> Ready -------> Running --------> Blocking --------> ...
就绪队列:
所有处于 “ ready ” 状态的进程,都存在一个“ 后备队列”
调度策略:
进程是 任务的调度最小单位,负责资源的分配和管理
分时系统:
调度策略以"时间片轮转",为主要的策略的系统"时间片轮转" : 分时, 每一个进程执行一段时间(时间片)
实时系统:
调度策略以“实时策略”为主的系统
“实施策略”:每次调度都去优先级高的那个进程,直到这个进程执行完毕或它主动放弃CPU或者比他更高的优先级的进程抢占。
抢占: 插队
进程的执行
进程执行的第一件事就是申请一块空间来存储程序的数据,不同的‘数据’ ,需要分区域保存。
linux进程的地址空间布局
“分段” : 分不同的逻辑区域
Linux对进程的数据进行分段管理,不同的属性的数据,存储在
不同的“内存段”中, 不同的“内存段”的属性是不同的,so不同的
“内存段”的管理的方式也不一样。
.text
主要是用来存放代码。
只读并且共享。这段内存在进程运行期间,不会释放的“代码段” 随进程持续性。
.data
数据段。
主要存放程序的 已经初始化的全局变量 和 已经初始化的static变量
可读可写。这段内存在进程运行期间,一直存在,随进程持续性的。
.bss
数据段。
主要存放程序的 没有初始化的全局变量 和 没有初始化的static变量
可读可写。这段内存在进程运行期间,一直存在,随进程持续性的。.bss,在进程初始化的时候,(可能)全部初始化为0。
.rodata :
只读数据段
主要存放程序中的只读数据的(如: 字符串常量)
只读。这段内存在进程运行期间,一直存在。随进程持续性。
栈(Stack)空间:
主要存放局部变量(非static的局部变量)
可读可写。这个段空间,会自动释放(代码块执行完啦,代码中的局部变量的空间就会被自动释放)。随代码块持续性的。
堆(Heap)空间: 动态内存空间
主要是malloc/realloc/calloc动态分配的空间。
可读可写。这段内存在进程运行期间,一旦分配,就会一直存在,直到你手动free或进程消亡。
防止 "内存泄漏" / "垃圾内存"
linux下面关于进程的一些API函数
1.创建一个进程
./test
运行一个 .c 编译的程序
在程序中 创建子进程
函数原型
pid_t fork(void);
返回值:
成功父进程返回 子进程pid (> 0)
成功子进程返回 0
失败返回 -1 同时error被设置
子进程会复制父进程的fork以下的所有代码,以及缓存区的所有内容,和定义的变量。
函数:
pid_t getpid(void);//用于获取自己的进程ID
pid_t getppid(void);// 用于获取父进程的ID
2.进程退出
2.1.自杀
a、main函数返回,进程退出
b、在进程执行的任意时刻,调用进程退出函数
void exit(int status) //正常退出,做一些清理工作
void _exit (int status) // 非正常退出
2.2 他杀
一般是被操作系统杀死
3.等待子进程退出
fork()执行之后,父子进程的执行先后同样是由系统调用,可能父进程执行完
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
这两个函数用来等待某个(些)子进程的状态发生改变的,等待 的状态发生改变有三种情况:
a、子进程退出(正常退出):main函数返回/exit/_exit
b、子进程被信号中止<>
c、子进程被信号唤醒(Blocking -> Ready)
一个进程的退出,操作系统会释放它的大部分的资源,但是有一小部分必须留给它父进程去释放。在子进程正常退出(a)情况,调用wait/waitpid 可以释放子进程的资源。
未使用此函数可能导致:两种进程的产生。
僵死(僵尸)进程:
如果一个进程退出了,但是父进程没有调用wait/waitpid,这个进程就会变成僵尸进程:已经死啦,但是资源没有被完全释放掉。
孤儿进程:
父进程结束,子进程还没结束。
函数:
----------------------------------------------------------------
pid_t wait(int *wstatus);
wstatus: 指针。用来保存子进程的退出信息的(退出码)
返回值:
成功返回退出的子进程的ID
失败返回-1,errno被设置。
-----------------------------------------------------------------
pid_t waitpid(pid_t pid, int *wstatus, int options);
pid : 指定要等待的进程或进程组
pid == -1, 表示等待任意的子进程退出
pid == 0 , 表示等待与调用进程同组的任意子进程
"进程组":
就是是一组进程。每一个进程必定会属于一个进程组。并且每个进程组,都会有一个组长进程,
一般来说,创建这个进程组的进程为组长,进程组有一个组ID,这个组ID,就是进程组长的ID.
pid < -1, 表示等待组ID等于pid的绝对值的那个组的任意子进程退出。
如:
pid == -1028
等待进程组号1028的那个组内的任意的子进程
pid > 0 , 表示等待pid指定的子进程
如:
pid == 1024
等待pid为1024的这个子进程
status: 同上
options : 等待选项
0 : 表示阻塞等待(默认阻塞等待)
WNOHANG: 非阻塞,假如没有子进程退出,则立刻返回。
返回值:
成功返回退出的子进程的ID
失败返回-1,errno被设置。