0
点赞
收藏
分享

微信扫一扫

linux-进程的初步认识

天涯学馆 2022-02-25 阅读 83
linux

1. 什么是进程?

  1. 进程是一个运行着的程序,它包含了程序在运行时的各个资源,进程是linux进行调度的基本单位,也是一个程序运行的基本单位。
  2. 进程就好比多任务,在我们编程中,进程就好比多个main在同时执行.

2. 进程状态

进程是程序的执行过程,根据它的生命周期可以划分成 3 种状态。

执行态:
	该进程正在运行,即进程正在占用 CPU, 任何时候都只有一个进程。
就绪态:
	进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间片。
等待态:
	进程正在等待某些事件,当前不能分配时间片,进程不能使用 CPU,
	若等待事件发生(等待的资源分配到)则可将其唤醒,变成就绪态。

3. 父子进程

3.1 PID和PPID

函数功能:
    获取自己的进程PID
    获取父进程的PPID
函数头文件:
    #include <sys/types.h>
    #include <unistd.h>
函数原型:
    pid_t getpid(void);
    pid_t getppid(void);
函数参数:
    无
函数返回值:
    返回的就是你需要的PID号

代码:

void Test_Pid(void)
{
    pid_t pd,ppd;
    pd = getpid();
    ppd = getppid();
    printf("自己的进程号==%d\n",pd);
    printf("父亲的进程号==%d\n",ppd);
}

结果:
在这里插入图片描述

3.2 fork

函数功能:在一个进程中创建一个子进程
函数头文件:<unistd.h>
函数原型:
        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 father\n");
        printf("father 返回进程号==%d\n",pd);
        printf("father 自己的进程号==%d\n",pd1);
        printf("father 父亲的进程号==%d\n",pd2);
        printf("a == %d\n",a);
    }else //子进程
    {
        pd1 = getpid();
        pd2 = getppid();
        printf("i am son\n");
        printf("son 返回进程号==%d\n",pd);
        printf("son 自己的进程号==%d\n",pd1);
        printf("son 父亲的进程号==%d\n",pd2);
        a++;
    }
}

结果:
在这里插入图片描述

3.3 vfork

函数功能:在一个进程中创建一个子进程
函数头文件:<unistd.h>
函数原型:
        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 father\n");
        printf("father 返回进程号==%d\n",pd);
        printf("father 自己的进程号==%d\n",pd1);
        printf("father 父亲的进程号==%d\n",pd2);
        printf("a == %d\n",a);
    }else //子进程
    {
        pd1 = getpid();
        pd2 = getppid();
        printf(" i am son\n");
        printf("son 返回进程号==%d\n",pd);
        printf("son 自己的进程号==%d\n",pd1);
        printf("son 父亲的进程号==%d\n",pd2);
        a++;
        exit(0);
    }
}

结果:
在这里插入图片描述
如果exit(0)屏蔽掉:
在这里插入图片描述

4. 孤儿进程

代码:

//测试孤儿进程
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 father\n");
        printf("father 返回进程号==%d\n",pd);
        printf("father 自己的进程号==%d\n",pd1);
        printf("father 父亲的进程号==%d\n",pd2);
        printf("a == %d\n",a);
        exit(0);
    }else //子进程
    {
        sleep(2);
        pd1 = getpid();
        pd2 = getppid();
        printf("i am son\n");
        printf("son 返回进程号==%d\n",pd);
        printf("son 自己的进程号==%d\n",pd1);
        printf("son 父亲的进程号==%d\n",pd2);
        a++;
    }
}

结果:
在这里插入图片描述

5. 僵尸进程

子进程退出,父进程没有做清理工作。一种非常特殊的进程,它几乎已经 放弃了所有内存空间,没有任何可执行代码, 也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供 其他进程收集,除此之外,僵尸进程不再占有任何内存空间,父进程退出会清理子进程 。

这个僵尸进程需要它的父进程来为它收尸,如果他的父进程没有处理这个僵尸进程的措施,那么它就一直保持僵尸状态,如果这时父进程结束了,那么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 father\n");
        printf("father 返回进程号==%d\n",pd);
        printf("father 自己的进程号==%d\n",pd1);
        printf("father 父亲的进程号==%d\n",pd2);
        printf("a == %d\n",a);
        while(1);
 //		exit(0);       
    }else //子进程
    {
        pd1 = getpid();
        pd2 = getppid();
        printf(" i am son\n");
        printf("son 返回进程号==%d\n",pd);
        printf("son 自己的进程号==%d\n",pd1);
        printf("son 父亲的进程号==%d\n",pd2);
        a++;
    }
    exit(0);
}

结果:
在这里插入图片描述
清除僵尸进程:
在这里插入图片描述

6.防止出现孤儿进程

进程的等待:
    wait:等待子进程退出才会往下执行
    整个程序会挂起

函数功能:等待任意的子进程结束
函数头文件:
        <sys/types.h>
        <sys/wait.h>
函数原型:
         pid_t wait(int *status);
函数参数:
        给NULL
函数返回值:
    成功返回终止的那个子进程的pid
    失败返回-1
函数功能:等待指定的子进程结束
函数头文件:
        <sys/types.h>
        <sys/wait.h>
函数原型:
    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 father\n");
        wait(NULL);//回收子进程
        //防止子进程出现孤儿进程
        printf("father 返回进程号==%d\n",pd);
        printf("father 自己的进程号==%d\n",pd1);
        printf("father 父亲的进程号==%d\n",pd2);
    }else//子进程
    {
        sleep(1);
        printf("i am son\n");
        printf("son 返回进程号==%d\n",pd);
        printf("son 自己的进程号==%d\n",pd1);
        printf("son 父亲的进程号==%d\n",pd2);
    }
    
}

结果:
在这里插入图片描述

7. 进程的死亡清理函数

会注册一个函数
当进程死掉的时候而且是正常死掉会去执行这个函数
函数功能:注册死亡函数
函数头文件:#include <stdlib.h>
函数的原型:int atexit(void (*function)(void));
函数的参数:
            空参空返回值的函数指针
函数返回值:  
            0  成功
            -1 失败

代码:

//测试死亡函数
void test_fcution(void)
{
    printf("i am death function\n");
    exit(0);
}
void Test_atexit(void)
{
    char * str ="hello";
    atexit(test_fcution);//注册死亡函数
    printf("函数注册完毕\n");
    exit(0);
}

结果:
在这里插入图片描述

8.继承函数

execl:
提供机制
当程序将要死亡的时候
调用该函数,会产生一个新的进程
来替代该程序的所有空间资源,包括进程号
提供了从一个进程启动另一个进程

函数API
函数功能:继承一个进程的空间
函数头文件: #include <unistd.h>
函数原型:
    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==%d\n",pid);
    printf("正常执行打进程的ppid==%d\n",ppid);
    execlp("ls","NULL",(char *)NULL);
    printf("hello world\n");
}

结果:
在这里插入图片描述

总结

在这里插入图片描述

举报

相关推荐

0 条评论