进程初认识
进程的概念(比较抽象):程序运行的实体
作为资源分配的基本单元
进程的理解:什么是进程
//hello.c
#include <stdio.h>
#include <unistd.h>//操作系统头文件
int main()
{
printf("change world\n");
while(1)
{
//Windows下sleep();休眠毫秒ms
sleep(1);//Linux下休眠单位秒s
//usleep(10000);//Linux下休眠单位微秒us
}
return 0;
}
我们知道运行这个程序后,是进入一个死循环,但我们在另外一个终端查看进程状态时
查看进程状态:ps 指令
ps aux (以该用户主形式查看所有终端机下所有用户的进程)
ps aux | grep hello
//以下是对ps后各个字段的解释
USER (所属用户)
PID (进程PID进程唯一标识)
%CPU (cpu使用率)
%MEM (物理内存使用率)
VSZ (虚拟内存使用情况)
RSS (物理内存使用情况)
TTY (终端的次要装置号码)
STAT (进程状态)
START (进程创建时间)
TIME (进程执行时间)
COMMAND(进程名)
程序:可执行的二进制文件(磁盘中)
进程:可执行的二进制文件(内存中)+进程PCB
进程pcb
task_struct一个描述进程的结构体
1.标识符:描述本进程的唯一标识,用来区别其他进程
2.状态:进程状态,退出代码,
3.优先级:进程相对于其他进程的优先级别 ,通过修改nice(NI)值,一般为-20到19 40个选项
4.程序计数器:程序中即将被执行的的下一条指令的地址,(cpu中的eip寄存器也有一份)
5.内存指针:程序代码和进程相关数据的指针,包括和其他进程共享的内存的指针
6.上下文数据:进程执行时cpu中的寄存器中的数据(保存进程执行的相关状态)
7.I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和进程使用的文件列表
8.记账信息:处理器执行时间总和,时间限制,记账号等
下面详细介绍以下进程标识符:
pid_t pid;//子进程标识符
pid_t ppid;//父进程标识符
pid_t uid;//用户标识符
pid_t git;//组标识符
pid_t euid;//有效用户标识符
例如:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid=getpid();
pid_t ppid=getppid();
printf("%d\n%d\n",pid,ppid);
printf("hello world");
while(1)
{
//Windows下sleep();休眠毫秒ms
sleep(1);//Linux下休眠单位秒s
//usleep(10000);//Linux下休眠单位微秒us
}
return 0;
}
用下面命令进行查看进程的父进程,可以看到其父进程为bash进程(用户命令与操作系统之间的媒介)
下面详细介绍进程的状态:
R (running) 0就绪状态,包含正在执行或者就绪状态(一切资源就绪只差分配cpu)
S (Sleeping) 1睡眠状态,中断睡眠,可以被唤醒,例如调用sleep();
D (disksleep) 2 深度睡眠状态,磁盘休眠状态,不可唤醒,不可kill掉,例如,该进程正在密集的进行I/o
T (stopped) 4 暂停状态,可以通过SIGTOP,将进程由S ,R状态转换为T状态,例如pause,挂起状态
t (tracingstop) 8 跟踪状态
X (dead) 死亡状态,一般是看不见的
Z (zombie)僵尸进程,当进程退出后,并且父进程没有读取到子进程的返回代码,就会产生僵尸进程。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t ret=fork();//创建一个进程,有两个返回值,
if(ret==0){
printf("ret:%d\n",ret);
printf("this if a child process,the pid id %d\n",getpid());
sleep(1); }
else
{if(ret>0){
printf("ret:%d\n",ret);
printf("this is a parent process,the pid is %d\n",getpid());
while(1)
{;}
}
else
perror("fork");
}
return 0;
}
这里可以看到子进程为Z僵尸状态。
而且,我们发现僵尸进程是不能直接kill掉的
我们可以将僵尸进程的父进程kill掉
其实这里是,将僵尸进程的父进程kill掉之后,子进程就会成为孤儿进程并且被1号进程领养,
1号进程会接收到该孤儿进程的退出码,这时的僵尸进程正常结束了。
在这里补充一下:
因为我们在windows下很少看到cpu占用率为100%,因为windows下是这样来计算的,例如,一个5核的处理器,当只有一个被全部使用时,这时可以看到cpu利用率是 20%,两个cpu被全部使用时,这时的cpu利用率为40%,以此类推。
但是在Linux下,同样是5核的处理器,当只有一个被全部使用时,显示cpu的利用率为100%,两个被全部使用时,这时的cpu利用率为200%.以此类推
进程优先级:
中的pri一项为进程的优先级,这个数字越小,表示优先级越高,低于80
Ni一项为优先级的修正值,其实我们是没有办法直接修改一个进程的优先级,例如最后一项ni为-20 ,其优先级为60 其实就是80-20;
创建进程:
pid_t fork();
两个返回值,一个在父进程中返回,一个在子进程中返回,会将父进程复制一次,并且进程从fork();后面开始执行(进程上下文数据中包含eip,保存了下一条执行的地址)。
父进程中返回子进程的pid;
子进程返回0:
例如:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t ret=fork();//创建一个进程,有两个返回值,
if(ret==0){
printf("ret:%d\n",ret);
printf("this if a child process,the pid id %d\n",getpid());
}
else
{
if(ret>0){
printf("ret:%d\n",ret);
printf("this is a parent process,the pid is %d\n",getpid());
}
else
printf("fork error\n");
}
return 0;
}
对fork()的总结:
1.一次调用有两个返回值,父进程返回子进程的pid,子进程返回0;
2.父进程和子进程都从fork()结束的下一条开始执行;
3.子进程以父进程为模板(PCB,数据各自有一份和代码共享),采用写实拷贝;
4.父子进程之间并发执行,顺序不确定,取决去操作系统调度;
5.fork()失败可能的原因是内存不够或者进程数量太多。