1、进程的概念
程序是存放在存储介质上的一个可执行文件;
当一个程序运行时被称之为进程;进程是一个程序的一次执行过程;
程序是静态的,他是一些保存在磁盘上的指令的有续集合。
进程是一个动态的概念,他是程序执行的过程,包括创建、调度、和消亡。
进程是程序执行和资源管理的最小单位。
2、进程的内存管理
当创建或者运行一个新的进程时,操作系统都会在内存中给当前进程分配空间,但是为了更好的解决进程之间的交互问题,系统给每一个进程分配的内存空间称之为虚拟内存,在32位操作系统中,每一个进程所占的虚拟内存是4G,4G的虚拟内存又分为1G的内核空间和3G的用户空间,内核空间时当前主机中所有进程共有的,用户空间是当前进程私有的。
虚拟内存与物理内存的关系是互相映射的,有时间单独总结一期。
3、进程号
每一个进程都设有一个进程号来标识的,器类型为pid_t,进程号的范围:0~32767。
进程号总是唯一的,但是金超可以重用。当一个进程终止后,其进程号就可以在此使用了。
进程号是由操作系统自动分配的
特殊的进程号:
0:内核进程,是当前系统开启是最先创建的进程
1:init进程,是所有的进程的祖先
4、Linux中进程的类型
交互进程:
改类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。执行程序后,命令行界面ctrl +z 将进程软中断,bg将进程运行到后端,在此输入fg将进程在此调到前端。
批处理进程:
该类进程不属于某个终端,他会提交到一个队列中一遍顺序执行。
守护进程:
该类进程在后台运行,他一般在linux启动时开始执行,系统关闭时才结束。
特殊的进程:
孤儿进程、守护进程、僵尸进程;
父进程退出,子进程没有退出,此时子进程成为孤儿进程。守护进程是特使的孤儿进程。当父进程创建子进程后,子进程退出,但子进程的资源没有释放,此时的子进程成为僵尸进程,僵尸进程会消耗资源。
相关命令:
查看进程使用ps命令;ps aux或ps -ef
杀死进程kill 进程号;kill all 进程名;
4、进程状态及转换
进程整个声明周期可以简单划分为三种状态:
就绪态:
进程已经具备执行一切条件,正在等待分配cpu的处理时间。
执行态:
该进程正在占用CPU运行。
等待态:
进程因不具备某些执行条件而暂时无法继续执行的状态。
进程的调度:时间片轮转,上下文切换。
5、创建进程
1、fork()
#include <unistd.h>
pid_t fork(void);
功能:在已有的进程基础上有创建一个子进程
参数:
无
返回值:
成功:
>0 子进程的进程号,标识父进程的代码区
0 子进程的代码区
失败:
-1 返回给父进程,子进程不会创建
例如:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//使用fork创建一个子进程
//只要执行fork,就会创建子进程
//通过fork的返回值来决定父进程如何运行,子进程如何运行
//进程之间是来回切换执行的,父子进程没有先后顺序可言
pid_t pid;
if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) //父进程的代码区
{
printf("This is parent process\n");
sleep(1);
printf("***************\n");
}
else //子进程的代码区
{
printf("This is child process\n");
sleep(1);
printf("---------------\n");
}
//父子进程的代码区不仅限于fork的返回值对应的if、else语句
//要分析父子进程什么时候结束才不会再运行
printf("nihao chengdu\n");
return 0;
}
父子进程之间的关系
#include <stdio.h>
#include <unistd.h>
int a = 100;
int main(int argc, char const *argv[])
{
static int b = 200;
int c = 300;
//当fork函数执行结束后,会创建一个子进程
//子进程会复制父进程的所有的虚拟内存,此时子进程和父进程再没有任何联系
//每一个进程拥有私有的用户空间,所以子进程创建后,不管父进程如何操作(对用户空间的操作)
//都不会影响子进程
//总结:父子进程公有的是fork之前的数据,fork之后
//不管如何操作自己的用户空间,相互都没有影响
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) //父进程
{
printf("This is parent process\n");
a++;
b++;
c++;
printf("parent process: %p -- %p -- %p\n", &a, &b, &c);
printf("parent process: %d, %d, %d\n", a, b, c);
}
else //子进程
{
printf("This is child process\n");
printf("child process: %p -- %p -- %p\n", &a, &b, &c);
printf("child process: %d, %d, %d\n", a, b, c);
}
while(1)
;
return 0;
}
2、getpid()/getppid()
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:获取当前进程的进程号
参数:
无
返回值:
成功:当前进程的进程号
pid_t getppid(void);
功能:获取当前进程的父进程的进程号
参数:
无
返回值:
常规:当前进程的父进程的进程号
例如:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid;
if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;
}
else if(pid > 0) //父进程
{
printf("This is parent process\n");
printf("pid = %d\n", pid);
printf("parent: pid = %d, ppid = %d\n", getpid(), getppid());
}
else //子进程
{
printf("This is child process\n");
printf("child: pid = %d, ppid = %d\n", getpid(), getppid());
}
while(1)
;
return 0;
}
3、exit()/_exit()
#include <unistd.h>
void _exit(int status);
功能:退出当前进程
参数:
status:退出状态,由父进程通过wait函数接收这个状态
一般失败退出设置为非0
一般成功退出设置为0
返回值:
无
#include <stdlib.h>
void exit(int status);
功能:退出当前进程
参数:
status:退出状态,由父进程通过wait函数接收这个状态
一般失败退出设置为非0
一般成功退出设置为0
返回值:
无
例如:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void myfun()
{
printf("nihao chengdu");
//return只能在主函数中用于退出进程,在子函数中只能退出函数
//return ;
//使用exit退出进程
//exit函数可以刷新缓冲区,常用
exit(0);
//使用_exit退出进程
//_exit函数不会刷新缓冲区
//_exit(0);
printf("nihao beijing\n");
}
int main(int argc, char const *argv[])
{
printf("hello world\n");
myfun();
printf("hello kitty\n");
return 0;
}
4、wait()/waitpid()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:阻塞等待子进程退出
参数:
status:子进程退出时如果使用exit函数,
那么子进程的退出状态会被当前参数接收到
如果不想接收子进程的退出状态或者子进程没有调用exit
则这个值设置为NULL
返回值:
成功:退出的子进程的进程号
失败:-1
pid_t waitpid(pid_t pid, int *status, int options);
功能:阻塞等待子进程退出
参数:
pid:
<-1 等待指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值
-1 等待任一子进程,此时waitpid和wait作用一样
0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会等待它
>0 等待进程ID等于pid的子进程
status:子进程退出时如果使用exit函数,
那么子进程的退出状态会被当前参数接收到
如果不想接收子进程的退出状态或者子进程没有调用exit
则这个值设置为NULL
options:选项
0 阻塞
WNOHANG 非阻塞
返回值:
成功:退出的子进程的进程号
失败:-1
wait(NULL) <==> waitpid(-1, NULL, 0)
例如:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t pid;
if((pid = fork()) < 0)
{
perror("fail to fork");
exit(1);
}
else if(pid > 0) //父进程
{
printf("This is parent process\n");
//wait:阻塞等待子进程退出,如果子进程不退出,他会一直阻塞下去
int status;
//wait(&status);
waitpid(-1, &status, 0);
printf("status = %#x\n", status);
printf("hello world\n");
}
else //子进程
{
printf("This is child process\n");
sleep(5);
//exit(2);
}
return 0;
}