一、进程1.基本概念 课本概念:程序的一个执行实例,正在执行的程序等 内核观点:担当分配系统资源的实体
描述进程——PCB 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合 课本上之所以称之为PCB(process control block),Linux操作系统下的PCB是task_struct
task_struct——PCB的一种(是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息)task_struct内容分类 标识符:描述本进程的唯一标识符,用来区别其他进程 状态:任务状态,退出代码,退出信息等 优先级:相对于其他进程的优先级 程序计数器:程序中即将被执行的下一条指令的地址
内存指针:包含程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据:进程执行时处理器的寄存器中的数据
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
记账信息:可能包含处理器时间总和,使用的时钟数总和,时间限制,记账号等
其他信息
2.组织进程 可以在内核源代码里找到它,所有运行在系统里的进程都以task_struct链表的形式存在内核里
3.查看进程 进程的信息可以通过/proc系统文件夹查看
如要获取PID为1的进程信息,需要查看/proc/1这个文件夹:
通过系统调用来获取进程标识符 系统为每一个进程分配一个唯一的标识号,这个标识号就称为进程的ID(process identifer)
进程id(PID)
父进程id(PPID) #include
#include
#include
int main()
{
printf("pid:%d
",getpid());
printf("ppid:%d
",getppid());
return 0;
}
4.通过系统调用创建进程——fork()初识进程创建的一般过程: 1.分配PID
2.分配PCB
3.将父进程环境复制过来
4.将父进程地址空间的内容拷贝到自己的地址空间
5.放入就绪队列 使用man手册认识fork():
返回值:
父子进程代码共享,数据各自开辟空间 #include
#include
#include
int main()
{
int ret=fork();
printf("hello proc:%d,ret:%d
",getpid(),ret);
return 0;
}
fork()之后通常要用if分流:
#include
#include
#include
int main()
{
int ret=fork();
if(ret<0){
perror("fork");
return 1;
}
else if(ret==0){ //child
printf("I am child:%d,ret:%d
",getpid(),ret);
}
else{ //father
printf("I am father:%d,ret:%d
",getppid(),ret);
}
sleep(2);
return 0;
}
二、程序和进程 程序:完成特定任务的一系列指令集合 进程:从用户角度看,进程是程序的一次动态执行过程;从操作系统角度看,进程是操作系统分配资源的基本单位,也是最小单位 每个进程都有自己的状态和独立的地址空间 进程需要有相关的数据结构来管理,操作系统通过PCB感知一个进程的存在
程序:数据+代码
进程:数据+代码+堆栈+PCB
进程和程序的区别: 进程是动态,程序是静态
进程的生命周期短暂,程序永久
进程有重要的数据结构PCB
一个进程只能对应一个程序,一个程序可以对应多个进程三、进程状态
1.进程的几种状态 R 运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
S 睡眠状态(sleeping):意味着进程在等待事件完成(也可叫做可中断睡眠 interruptible sleep)
D 磁盘休眠状态(Disk sleep):有时也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待 I/O的结束
T 停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止(T)进程,这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行
X 死亡状态(dead):这个状态只是一种返回状态,你不会在任务列表里看到这个状态
2.进程状态的修改 通过kill演示一下T、R状态:
· kill -l:查看系统支持的信号列表
· ps -l:查看进程信息
· kill -SIGSTOP pid:将该进程状态改为T状态
· kill -SIGCONT pid:将该进程状态改为R状态
Z(zombie)僵尸进程 · 僵尸状态(zombie)是一个比较特殊的状态,当进程退出并且父进程(使用wait()系统调用)没有读到子进程退出的返回代码时就会产生僵尸进程
· 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态码
· 所以,只要子进程退出父进程还在运行,但父进程中没有读取子进程状态,子进程进入Z状态
举例:维持30秒僵尸进程 #include
#include
int main()
{
pid_t id=fork();
if(id<0){
perror("fork");
return 1;
}
else if(id>0){
printf("parent [%d] is sleeping...
",getpid());
sleep(30);
}
else{
printf("child [%d] is begin z...
",getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
编译运行并在另一个终端下启动监控:
测试结果:
可以看到子进程5秒后由S状态进入僵尸状态
僵尸进程危害 · 进程的退出状态必须被维持下去,父进程若一直不读取,子进程将一直处于Z状态
· 维护退出状态本身就是要用数据维护,也属于进程的基本信息,所以保存在PCB中,即Z状态一直不退出,PCB一直都要维护
· 若一个父进程创建了很多子进程,就是不回收,就会造成资源的浪费,因为数据结构本身就要占用内存,就像C语言中定义一个结构体变量,是要在内存的某个位置开辟空间
· 造成内存泄露
孤儿进程 父进程如果提前退出,那么子进程后退出,进入Z状态之后,该如何处理?
父进程先退出,子进程就被称为孤儿进程
孤儿进程被1号init进程“领养”(当然要有init进程回收)举例: #include
#include
#include
int main()
{
pid_t id=fork();
if(id<0){
perror("fork");
return 1;
}
else if(id==0){
printf("I am child,pid:%d
",getpid());
sleep(10);
}
else{
printf("I am father,pid:%d
",getpid());
sleep(3);
exit(0);
}
return 0;
}
测试结果: