- 进程是一个运行着的程序,它包含了程序在运行时的各个资源,进程是linux进行调度的基本单位,也是一个程序运行的基本单位。进程就好比多任务,在我们编程中,进程就好比多个main在同时执行.
进程是程序的执行过程,根据它的生命周期可以划分成 3 种状态。
执行态: 该进程正在运行,即进程正在占用 CPU, 任何时候都只有一个进程。 就绪态: 进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。 等待态: 进程正在等待某些事件,当前不能分配时间片,进程不能使用 CPU, 若等待事件发生(等待的资源分配到)则可将其唤醒,变成就绪态。3. 父子进程 3.1 PID和PPID
函数功能:
获取自己的进程PID
获取父进程的PPID
函数头文件:
#include
#include
函数原型:
pid_t getpid(void);
pid_t getppid(void);
函数参数:
无
函数返回值:
返回的就是你需要的PID号
代码:
void Test_Pid(void)
{
pid_t pd,ppd;
pd = getpid();
ppd = getppid();
printf("自己的进程号==%dn",pd);
printf("父亲的进程号==%dn",ppd);
}
结果:
函数功能:在一个进程中创建一个子进程 函数头文件:函数原型: pid_t fork(void) 函数参数: 无 函数返回值: 在本进程(父进程) 返回子进程的ID 在子进程 返回 0 返回负值 表示创建子进程失败 特点: 该函数会完全复制父进程的所有资源 但是会从fork之下开始运行 子进程所独有的只有它的进程号
代码:
//测试创建子进程函数fork()
void Test_fork(void)
{
int a=10;
char * str = "hello";
pid_t pd;
pid_t pd1,pd2;
printf("测试........n");
pd = fork();//从这句话开始创建子进程
if(pd>0)//父进程
{
sleep(2);
pd1 = getpid();
pd2 = getppid();
printf("i am fathern");
printf("father 返回进程号==%dn",pd);
printf("father 自己的进程号==%dn",pd1);
printf("father 父亲的进程号==%dn",pd2);
printf("a == %dn",a);
}else //子进程
{
pd1 = getpid();
pd2 = getppid();
printf("i am sonn");
printf("son 返回进程号==%dn",pd);
printf("son 自己的进程号==%dn",pd1);
printf("son 父亲的进程号==%dn",pd2);
a++;
}
}
结果:
函数功能:在一个进程中创建一个子进程 函数头文件:函数原型: pid_t vfork(void) 函数参数: 无 函数返回值: 在本进程(父进程) 返回子进程的ID 在子进程 返回 0 vfork: 这个函数跟fork功能基本一致 只不过fork真正的创建了两个进程 而vfork虽然也是创建了两个进程 但是本质上来讲它只创建了一个 *会阻塞父进程 *必须等到子进程运行完毕(死掉) *他才开始运行 *子进程跟父进程共占一个资源 *子进程结束exit函数之后 才会去运行父进程否则会发生段错误 fork:子进程拷贝父进程的数据段、堆栈段 vfork:父子进程共享数据段
代码:
//测试创建子进程函数vfork()
void Test_vfork(void)
{
int a=10;
char * str = "hello";
pid_t pd;
pid_t pd1,pd2;
printf("测试........n");
pd = vfork();//从这句话开始创建子进程
if(pd>0)//父进程
{
pd1 = getpid();
pd2 = getppid();
printf(" i am fathern");
printf("father 返回进程号==%dn",pd);
printf("father 自己的进程号==%dn",pd1);
printf("father 父亲的进程号==%dn",pd2);
printf("a == %dn",a);
}else //子进程
{
pd1 = getpid();
pd2 = getppid();
printf(" i am sonn");
printf("son 返回进程号==%dn",pd);
printf("son 自己的进程号==%dn",pd1);
printf("son 父亲的进程号==%dn",pd2);
a++;
exit(0);
}
}
结果:
如果exit(0)屏蔽掉:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
子进程死亡需要父进程来处理,那么意味着正常的进程应该是子进程先于父进程死亡。当父进程先于子进程死亡时,子进程死亡时没父进程处理,这个死亡的子进程就是孤儿进程。
代码:
//测试孤儿进程
void Test_Guer(void)
{
int a=10;
char * str = "hello";
pid_t pd;
pid_t pd1,pd2;
printf("测试........n");
pd = fork();//从这句话开始创建子进程
if(pd>0)//父进程
{
pd1 = getpid();
pd2 = getppid();
printf("i am fathern");
printf("father 返回进程号==%dn",pd);
printf("father 自己的进程号==%dn",pd1);
printf("father 父亲的进程号==%dn",pd2);
printf("a == %dn",a);
exit(0);
}else //子进程
{
sleep(2);
pd1 = getpid();
pd2 = getppid();
printf("i am sonn");
printf("son 返回进程号==%dn",pd);
printf("son 自己的进程号==%dn",pd1);
printf("son 父亲的进程号==%dn",pd2);
a++;
}
}
结果:
子进程退出,父进程没有做清理工作。一种非常特殊的进程,它几乎已经 放弃了所有内存空间,没有任何可执行代码, 也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供 其他进程收集,除此之外,僵尸进程不再占有任何内存空间,父进程退出会清理子进程 。
这个僵尸进程需要它的父进程来为它收尸,如果他的父进程没有处理这个僵尸进程的措施,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。
试想一下,如果有大量的僵尸进程驻在系统之中,必然消耗大量的系统资源。但是系统资源是有限的,因此当僵尸进程达到一定数目时,系统因缺乏资源而导致奔溃。所以在实际编程中,避免和防范僵尸进程的产生显得尤为重要。
代码:
//测试僵尸进程
void Test_Z_jincheng(void)
{
int a=10;
char * str = "hello";
pid_t pd;
pid_t pd1,pd2;
printf("测试........n");
pd = fork();//从这句话开始创建子进程
if(pd>0)//父进程
{
pd1 = getpid();
pd2 = getppid();
printf(" i am fathern");
printf("father 返回进程号==%dn",pd);
printf("father 自己的进程号==%dn",pd1);
printf("father 父亲的进程号==%dn",pd2);
printf("a == %dn",a);
while(1);
// exit(0);
}else //子进程
{
pd1 = getpid();
pd2 = getppid();
printf(" i am sonn");
printf("son 返回进程号==%dn",pd);
printf("son 自己的进程号==%dn",pd1);
printf("son 父亲的进程号==%dn",pd2);
a++;
}
exit(0);
}
结果:
清除僵尸进程:
进程的等待:
wait:等待子进程退出才会往下执行
整个程序会挂起
函数功能:等待任意的子进程结束
函数头文件:
函数原型:
pid_t wait(int *status);
函数参数:
给NULL
函数返回值:
成功返回终止的那个子进程的pid
失败返回-1
函数功能:等待指定的子进程结束
函数头文件:
函数原型:
pid_t waitpid(pid_t pid,int * status,int options);
函数参数:
pid:你要等待的子进程的ID
status:NULL
函数返回值:
成功返回终止的那个子进程的pid
失败返回-1
代码:
//测试等待进程的函数(wait)
//防止出现孤儿进程
void test_wait(void)
{
pid_t pd1,pd2;
pid_t pd = fork();
pd1 = getpid();
pd2 = getppid();
if(pd > 0)//父进程
{
printf("hello i am fathern");
wait(NULL);//回收子进程
//防止子进程出现孤儿进程
printf("father 返回进程号==%dn",pd);
printf("father 自己的进程号==%dn",pd1);
printf("father 父亲的进程号==%dn",pd2);
}else//子进程
{
sleep(1);
printf("i am sonn");
printf("son 返回进程号==%dn",pd);
printf("son 自己的进程号==%dn",pd1);
printf("son 父亲的进程号==%dn",pd2);
}
}
结果:
会注册一个函数 当进程死掉的时候而且是正常死掉会去执行这个函数 函数功能:注册死亡函数 函数头文件:#include函数的原型:int atexit(void (*function)(void)); 函数的参数: 空参空返回值的函数指针 函数返回值: 0 成功 -1 失败
代码:
//测试死亡函数
void test_fcution(void)
{
printf("i am death functionn");
exit(0);
}
void Test_atexit(void)
{
char * str ="hello";
atexit(test_fcution);//注册死亡函数
printf("函数注册完毕n");
exit(0);
}
结果:
execl:
提供机制
当程序将要死亡的时候
调用该函数,会产生一个新的进程
来替代该程序的所有空间资源,包括进程号
提供了从一个进程启动另一个进程
函数API 函数功能:继承一个进程的空间 函数头文件: #include函数原型: int execl(const char *path, const char *arg, ...); 函数参数: path : 你要启动进程可执行的路径 const char *arg, ...: 函数的传参,NULL 返回值:成功函数没有返回,出错返回-1
代码:
//测试execl
void Test_execl(void)
{
pid_t pid =getpid();
pid_t ppid=getppid();
printf("正常执行打进程的pid==%dn",pid);
printf("正常执行打进程的ppid==%dn",ppid);
execlp("ls","NULL",(char *)NULL);
printf("hello worldn");
}
结果:



