一.进程相关的概念:
1.什么是程序?
答:编译好的二进制文件
2.什么是进程?
答:运行着的程序,
运行一系列指令的过程,
在操作系统角度:分配系统资源的基本单位
3.程序与进程的区别?
答:(1) 程序占用磁盘,不占用系统资源
(2) 内存占用系统资源
(3) 一个程序对应多个进程,一个进程对应一个程序
(4) 程序没有生命周期,进程由生命周期
4.单道程序设计与多道程序设计
单道:每次只能运行一个程序,串行方式进行
多道:微观上串行,宏观上并行
程序如何转化为进程?
当程序执行时,操作系统会将可执行程序复制到内存中。
- 内核将程序读入内存,为程序分配内存空间。
- 内核为该进程分配PID(进程标识符)和其他所需资源。
- 把进程放在运行队列中等待执行,程序转化为进程以后就可以被操作系统调度程序执行了。
虚拟内存映像:
虚拟内存映像是指内核在内存中如何存放可执行文件。
每当我们创建一个进程时,操作系统就会为该进程分配一个 4GB 大小的虚拟进程地址空间。
为什么是4G呢?
这是因为32位计算机cpu的最大寻址空间为4G
MMU的了解:
MMU又叫内存管理单元。
MMU作用:
1.用来做虚拟内存与物理内存的映射
2.设置修改内存访问级别
如下例:
我们在一块虚拟内存中int a = 1,MMU也会在对应的物理地址中找到一块映射过去的内存,实际上a是存在对应的物理地址上的。
cpu读取的时候是先找到对应的虚拟地址,然后通过MMU的映射关系,从而找到物理地址,然后将值取出。
PCB(进程控制块)的了解:
1.一个结构体struct task_struct,内部成员很多这里就不一以说明 ,其实是我也没有完全理解
重点说一说PCB,每一个进程在运行时都有一个唯一的进程控制块,
操作系统是根据PCB来对并发执行的进程进行控制和管理的,
PCB是进程存在的唯一标识。
PCB存在于上面说的虚拟内存映像中3G-4G的内核区中,
说一说这里面和文件I/O有关的东西,PCB中有一个文件描述符表,这个表中有1024个文件描述符,从0一直到1023,0,1,2对应标准输入,标准输出,标准错误,每次打开文件时都会用到最小的没有被用到的文件描述符,可以看看下面的例子:
#include <stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
close(1);//关闭了文件描述符1 == stdout
int fd = open("1.log",O_CREAT | O_TRUNC | O_WRONLY ,0644);
//此时的fd就是1了
printf("hello world\n");
//输出到fd指向的文件中
fflush(stdout);
//fflush用来刷新缓冲区,
//如果不刷新缓冲区,不能写入到文件中
//缓冲区存在与文件描述符结构体中
close(fd);
return 0;
}
这段代码中,printf函数会调用write函数,然后write函数会调用底层的sys_write函数,sys_write函数会调用设备驱动,也就是显示器。
二.进程相关函数
1.fork 函数—>(用来创建一个进程)
函数原型: pid_t fork(void);
函数返回值
- 失败: -1
- 成功: 两次返回值
父进程返回子进程的ID
子进程返回 0
2.获取进程ID函数
函数原型:
pid_t getpid(void)—>(获取当前进程的ID)
pid_t getppid(void)—>(获取当前进程父进程的ID)
3.查看进程信息与杀死进程:
init进程是所有进程的祖先
ps aux
ps ajx --可以追述进程之间的血缘关系
kill:
(1)给进程发送一个信号
(2)SIGKILL 9号信号
(3)kill -SIGKILL pid -->杀死进程
或者kill -9 pid
5.循环创建n个子进程
如果在循环中子进程不中断循环,将会出现问题,子进程会在循环中继续创建子进程,那我们就需要再fork完之后把每一次的子进程break除去
//循环创建了5个子进程
#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int n = 5;
int i = 0;
pid_t pid = 0;
for(i = 0; i < 5; i++)
{//父进程循环结束
pid = fork();
if(pid == 0)
{
//son
printf("I am child , pid = %d, ppid = %d\n",getpid(),getppid());
break; //子进程推出循环的接口
}
else if(pid > 0)
{
//father
//printf("I am father ,pid = %d, ppid = %d\n",getpid(),getppid());
}
}
sleep(i);
if(i < 5)
{
printf("I am child ,will exit,pid = %d,ppid = %d\n",getpid(),getppid());
}
else
{
printf("I am parent ,will out pid = %d,ppid = %d\n",getpid(),getppid());
}
return 0;
}
6.进程共享
父子进程之间在fork后,有哪些相同之处?那些不同之处?
父子相同处:全局变量
data
.text
堆
栈
环境变量
用户ID
宿主目录
进程工作目录
信号处理方式
父子不同处:1. 进程ID
2. fork返回值
3. 父进程ID
4. 进程运行时间
5. 定时器
6.未决信号集
父子进程满足读时共享,写时复制
7.exec族函数—>执行其他程序
函数所在头文件: #include<unistd.h>
函数原型:
int execl(const char *pathname, const char arg, …/ (char *)NULL */);
int execlp(const char *file, const char arg, …/ (char ) NULL/);
这里比上一个多了一个p,这个p是指的环境变量
file :要执行的程序
arg 参数列表:
参数列表最后一个需要一个NULL作为结尾
返回值:
只有失败才会返回
函数作用:
将当前进程的.text,data替换为所要加载的程序
的.text,data,然后让进程从新的.text第一
条指令开始执行,但是进程的ID不变,换核不换壳
直接把原来的代码段,换成自己的代码段,然后执行。
#include <stdio.h>
#include<unistd.h>
int main()
{
execlp("ls","ls","-l","--color=auto",NULL);
//execl ("/bin/ls","ls","-l","--color=auto",NULL);
perror("exec err");
printf("hello\n");
return 0;
}
8.孤儿进程与僵尸进程
孤儿进程(orphan):父亲死了,子进程被init进程领养
僵尸进程(zombie):子进程死了,父进程没有回收子进程的资源(PCB)
kill命令不能杀死僵尸进程
如何回收僵尸进程:杀死父亲,init领养,负责回收
9.子进程回收(知道子进程的死亡原因)
函数原型:
pid_t wait(int *wstatus);
wstatus: 传出参数
作用:
阻塞等待子进程死亡
回收子进程资源
查看死亡原因
函数返回值:
成功 返回终止的子进程ID
失败 返回-1
子进程的死亡原因:
正常死亡WIFEXITD
如果WIFEXITED为真,
使用WEXITSTATUS得到退出状态
非正常死亡WIFSIGNALED
如果WIFSIGNALED为真,
使用WTERMSIG得到信号
函数原型:
pid_t waitpid(pid_t pid, int *wstatus, int options);
pid:
<-1 组ID
=-1 回收任意
>0 回收指定的pid
=0 回收和调用进程组ID相同组内的子进程
options:
设置为0
与wait相同也会阻塞
设置为 WNOHANG
如果当前没有子进程退出的,会立即返回
函数返回值:
如果设置了WNOHANG,
那么如果没有子进程退出,返回0
否则,返回退出进程的pid
失败返回-1
#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == 0)
{
printf("I am child,will die!\n");
sleep(2);
//return 101;
//如果这里这么写,那么死亡方式就是正常死亡
while(1)
{
printf("No die!HaHa\n");
sleep(1);
}
}
else if(pid > 0)
{
printf("I am parent,wait for child die!\n");
int status;
pid_t wpid = wait(&status);
printf("wait ok,wpid = %d,pid = %d\n",wpid,pid);
//判断死亡方式:正常死亡
if(WIFEXITED(status))
{
printf("child exit with %d\n",WEXITSTATUS(status));
}
//信号杀死
if(WIFSIGNALED(status))
{
printf("child killedby%d\n",WTERMSIG(status));
}
while(1)
{
sleep(1);
}
}
return 0;
}