1、进程
1.1什么是进程:进行中的程序(正在运行中的程序)-process过程
程序的一次执行过程 - 进程
hello.c -- 程序源代码
a.out -- 可执行程序
1.2程序和进程的关系:
程序<------>进程
1.3进程怎么来的:
程序->加载到内存(运行起来了)-->进程
1.4总结:
进程 ---动态---内存
程序---静态----硬盘
1个程序可以对应一个或多个进程
1.5为什么需要进程
时间片轮转
若要保存上次程序运行到哪了,则需记录-程序的执行状态
则需要进程这个概念了
描述和记录,正在运行中的程序的相关信息。
进程的调度(操作系统,上下文的切换)(让它看起来程序在并发运行,其实每个程序只能运行一小会,且(单核)cpu每次只执行一个程序)
进程的执行特点:微观串行,宏观并行。
最核心的目的:提高效率。
1.6进程的组成:
1、
程序 = 代码 + 数据
bss --- 未初始化的数据 |
data --- 初始化了的数据 | 静态区 | ---数据
text --- 正文段(代码段)
bss -- 未初始化 栈
---- 堆
data -- 已初始化 --------------./a.out------------> bss
---- data
text -- 代码段 text
//linux系统下,程序的内存布局 //进程的实体
栈
堆
静态区
字符串常量区
代码段 //c语言标准的内存布局
进程 -----
pcb---是一个结构体 Process Control Block
进程 = pcb块 + 各个段(栈 | 堆 | bss | data | text)
1.7进程的生命周期:
通用三态模型:
linux系统下的状态模型:
1.8观察进程的命令:
1、top命令 :观察进程 //类似任务管理器
2、ps
man ps /state
进程状态代码:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue) ---就绪队列中
S interruptible sleep (waiting for an event to complete)
T stopped(暂停态),
either by a job control signal or because it is being traced.
*W paging (not valid since the 2.6.xx kernel)
*X dead (should never be seen)Z defunct ("zombie") process, terminated but not reaped(收尸) by its parent.
查看进程相关信息
1.就绪态、运行态 R
2.睡眠态、等待态
可唤醒等待态 S
不可唤醒等待态 D
3.停止态 T
4.僵尸态 Z
5.结束态
ps aux | grep 进程名 (pid号) //当前进程的id号 及 状态
此命令可以察看所有同名文件
ps -eLf | grep 进程名 (pid号) //可查看父进程id号
ppid(parent process id)//父进程id
3、pstree
pstree -sp id号 //是两个命令合起来 {pid号(如果不写表示查看全部的)}
查看进程关系
init(1) (1号进程最早出现的一个进程)---- gnome-terminal(xxxx)----bash(xxxx)
4、kill
kill PID
kill 信号编号(-数字) 进程ID
发送信号+PID对应的进程,默认接收者关闭
kill -9 进程PID号 //杀死程序
kill -l //查看信号列表
killall 信号编号 程序名字
5、 fork()
pid_t fork(); 叉子
一次调用,会返回两次。
子进程先运行和是父进程先进程,顺序不确定。
变量不共享。
子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。
功能:通过该函数可以从当前进程中克隆一个同名新进程。
克隆的进程称为子进程,原有的进程称为 父进程。
子进程是父进程的完全拷贝。
子进程的执行过程是从fork函数之后执行。
子进程与父进程具有相同的代码逻辑。
返回值:int 类型的数字。
在父进程中:成功 返回值是子进程的pid号 >0
失败 返回-1;
在子进程中:成功 返回值 0
失败 无
代码示例:
int main()
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork fail");
return -1;
}
if(pid >0 )
{
printf("father---\n");
}
else if (pid == 0 )
{
printf("child---\n");
}
}
输出:
代码解释:
父子进程独立操作。
练习:
1、父子进程都死循环。
a.结束父进程,查看当前系统中父子进程的状态
b.结束子进程,查看当前系统中父子进程的状态
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork fail");
return -1;
}
if(pid >0 )
{
while(1)
{
printf("father---\n");
}
}
else if (pid == 0 )
{
while(1)
{
printf("child---\n");
}
}
return 0;
}
a.结束父进程 //子进程成为后台进程,孤儿进程 --- 子进程还在,父进程不在。(init收养子进程。以前的版本)
b.结束子进程 //子进程结束,父进程还在 父进程没有去收尸 ---子进程呈僵尸态,若此时结束父进程,则子进程的实尸体被系统收养,但系统发现子进程已死则直接销毁回收,不然僵尸态(户口信息)会占用内存。
2、
自己分别定义一个 static的变量 static int a = 0; 全局变量 int b = 1; 堆区 int *p = (int *)malloc(sizeof(int)); *p = 2; (做修改)父进程中 做加1的操作 ,子进程中做加2的操作 分别打印,查看效果! 同时可以查看一下父子进程的关系(pstree) 父子进程的状态!
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int b = 1;
int main(int argc, const char *argv[])
{
static int a = 0;
int *p = (int *)malloc(sizeof(int));
*p = 2;
pid_t pid = fork();
if(pid < 0)
{
perror("fork fail");
return -1;
}
//父进程独立代码
while(1)
{
if(pid > 0)
{
printf("father a = %d\n",a++);
printf("father b = %d\n",b++);
printf("father p = %d\n",(*p)++);
}
//子进程独立代码
else if(pid == 0)
{
printf("child a = %d\n",a++);
printf("child b = %d\n",b++);
printf("child p = %d\n",(*p)++);
}
sleep(1);
}
return 0;
}
父子进程之间不会影响,谁先运行的顺序由操作系统决定,尽可能公平、高效
3、面试题:
一次fork生成几个进程?
他们之间的关系是什么样的? //生成1个一共有两个
如果两次fork同时前后执行,会生成几个进程? //共有(4)个进程
他们之间的关系如何表示,
有多少个子进程,
有没有孙进程?有
4、fork()&&fork()|| fork() 总共有几个进程?
有5个进程
5、
fork();
fork();
fork(); 一共有几个进程? // 2^n个进程
6、动态生成子进程,并打印输出各自进程的pid号
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork fail");
return -1;
}
if(pid >0 )
{
while(1)
{
printf("father---%d\n",getpid());
}
}
else if (pid == 0 )
{
while(1)
{
printf("child---%d\n",getpid());
}
}
return 0;
}