Linux中进程的创建,调度,状态及接口函数

 进程和程序的区别

     程序:保存在磁盘空间中的的一段代码的集合,是死的
    进程:是一个程序动态执行的过程,包括进程的创建、调度和消亡的过程

进程相关的命令 


    PID:进程的标识符(进程的ID) 
    PPID:父进程的ID号    
    
    1.top
      根据CPU占用率查看进程信息 
      q退出 

    2.ps -ef 
      查看当前所有进程的信息 

      ps -ef | grep a.out 查看a.out相关的进程信息

    3.ps -aux 
      查看当前所有进程的信息

      ps -aux | grep a.out  查看a.out相关的进程信息

    4.kill
      向进程发送信号,并让进程结束 
      
      kill -l 
      查看信号的类型 

      kill -信号名/信号编号 进程ID
      kill -9 进程ID 

      killall -信号名/信号编号 进程名 

    5.后台运行代码:
        ./a.out & 
    
    6.jobs 
      查看后台执行的任务

    7.fg 任务编号
      将任务编号对应的后台任务放到前台执行 

    8.nice 
        nice -n 10 ./a.out
        以10作为优先级运行a.out进程         在Linux系统中数字越小优先级越高

    9.renice
        renice -n 10 进程ID

    10.pstree
        查看进程的关系

进程的创建 

32bit操作系统中,进程运行,操作系统开辟 0 - 4G虚拟内存空间
    虚拟内存空间分为:
        用户空间:
            1.文本段:文本区 存放代码、指令
            2.数据段:数据区 存放全局变量、静态变量、常量
            3.系统数据段:堆、栈 

        内核空间:
            1.用户无法访问内核空间,通过信号、系统调用实现用户层和内核层的交互 

    实际物理内存空间:
        MMU内存映射单元实现虚拟地址和物理地址间的映射关系

    两个进程所占虚拟内存空间并不是 8 G,而是 4 G,两个进程分时共用同一空间
    两个进程物理内存空间是独立的,进程1和进程2空间独立

    结论:
        进程的空间是独立的,但是两个进程并不是8G

 进程的调度

   (微观串行,宏观并行)

进程调度算法:
        1.先来先执行,后来后执行
        2.高优先级调度算法
        3.时间片轮转调度算法   

               时间片:
                   CPU在一个进程任务中执行的时间称为时间片
        4.抢占式调度算法 
        5.多级反馈队列调度算法

 进程的状态 

       1.运行态、就绪态:  R
            运行态:正在被CPU执行的进程任务
            就绪态:正在执行的任务,但由于CPU调度还没有被调度到

        2.可唤醒等待态:    S
            睡眠态
            由于缺少资源导致程序阻塞的状态,当拿到资源后,继续向下执行

        3.不可唤醒等待态:  D
            不想被CPU任务调度打断 

        4.暂停态            T 
            用户主动让进程任务暂停(挂起)状态

        5.僵尸态            Z
            进程任务结束了,但空间没有被回收

        6.结束态             X  
            进程任务执行完了,空间也被回收了

进程相关的函数接口

(1)fork 

fork 
       pid_t fork(void);
       功能:
           创建一个子进程
       参数:
            void 缺省
       返回值:
           创建失败返回-1 
           成功父进程中返回创建的子进程的PID
           子进程中返回0 

        写时拷贝:fork创建子进程,子进程拷贝父进程中的数据,如果只是读取时不会为子进程单独生成空间,只有做写入操作时,才会为子进程开辟独立空间,提高进程创建的效率
 

 (2)getpid

getpid
      pid_t getpid(void);
      功能:
           获取进程的PID

    3.getppid  
      pid_t getppid(void);  
      功能:
           获得父进程的PID 
 

(3) exit

exit
      void exit(int status);
      功能:
            进程结束 ,会刷新缓存区
      参数:
            status:进程结束的状态码
      返回值:
            缺省

      主函数中使用exit等价于return 

 (4) _exit 

 _exit 
       void _exit(int status);
       功能:
            立即结束进程任务,不会刷新缓存区

进程消亡 

   僵尸态:进程代码执行结束,空间没有被回收

    僵尸进程产生原因:
        1.子进程任务结束,空间没有被回收,该进程成为僵尸进程

    如何避免产生僵尸进程:
        1.让父进程先结束,子进程再结束,子进程不会变成僵尸进程
        2.子进程结束,父进程回收子进程空间,子进程不会变成僵尸进程

    孤儿进程:
        父进程先结束,子进程成为孤儿进程,被init进程(1号)进程收养,子进程结束,init系统进程自动回收进程空间 

(5)wait 

 wait
    pid_t wait(int *wstatus);
    功能:
        等待子进程结束,回收子进程空间
    参数:
        wstatu s:存放子进程结束的状态码空间首地址
    返回值:
        成功返回子进程的PID
        失败返回-1

    注意:
        wait具有阻塞功能,如果子进程先结束,wait会不阻塞直接回收
                        如果子进程没结束,wait会阻塞直到子进程结束回收子进程空间

     WIFEXITED(wstatus):判断子进程是否正常结束
     WEXITSTATUS(wstatus):获取子进程正常结束的状态码
     WIFSIGNALED(wstatus):判断子进程是否被信号打断结束
     WTERMSIG(wstatus):获取子进程被信号打断结束的状态码

     实现同步功能

           将本来异步的父子进程变为同步的
 

#include"head.h"
int main()
{
    pid_t pid;
    int ret =0;
    int status;
    pid=fork();
    if(pid==-1)
    {
        perror("failed to fork");
        return -1;
    }
    if(pid==0)
    {
        printf("子进程PID:%d正在运行\n",getpid());
        sleep(10);
        printf("我运行完了\n");
        exit(10);//结束子进程
    }
    else if(pid>0)
    {
        printf("父进程PID:%d正在运行\n",getpid());
        ret=wait(&status);    // 等待子进程结束,回收子进程空间,不回收子进程空间,子进程会变成僵尸态。
        printf("子进程PID:%d被回收\n",ret);
        if(WIFEXITED(status))
        {
            printf("子进程正常结束,值为%d\n",WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status))
        {
            printf("子进程被%d信号杀死", WTERMSIG(status));
        }
    }
    return 0;
}

(6)waitpid

waitpid 
    pid_t waitpid(pid_t pid, int *wstatus, int options);
    功能:
        回收指定的子进程空间
    参数:
        pid:要回收的子进程的pid
        wstatus:回收状态的指针
        options:回收选项
            0 阻塞回收
            WNOHANG:非阻塞回收
    返回值:
        成功:返回回收的子进程的pid
        失败:返回-1
 

#include "head.h"
int main()
{
    pid_t pid1;
    pid_t pid2;
    pid_t ret=0;
    int status;
    pid1=fork();
    if(pid1==-1)
    {
        perror("failed to fork");
        return -1;
    }
    if(pid1==0)
    {
        printf("子进程1PID:%d正在执行\n",getpid());
        sleep(3);
        printf("子进程1即将结束\n");
        exit(0);

    }
    if(pid1>0)
    {
        pid2=fork();
        if(pid2==-1)
        {
            perror("failed to fork");
            return -1;
        }
        if(pid2==0)
        
        {
            printf("子进程2PID:%d正在执行\n",getpid());
            sleep(5);
            printf("子进程2即将结束\n");
            exit(0);
        }
        else if(pid2>0)
        {
            while(1)
            {
                ret=waitpid(-1,&status,WNOHANG);//回收第一个结束的进程,没结束的进程变为孤儿进程被系统回收
                if(ret==-1)
                {
                    perror("failed to waitpid");
                    return -1;
                }
                else if(ret==0)
                {
                printf("还没有子进程结束呢\n");
               
                }
                else if(ret>0)
                {
                    break;
                }
                sleep(1);
                
            }
            

             printf("子进程PID:%d被回收\n",ret);
               if (WIFEXITED(status))
            {
                printf("子进程正常结束,值为:%d\n", WEXITSTATUS(status));
            }
            else if (WIFSIGNALED(status))
            {
                printf("子进程被 %d 号信号杀死\n", WTERMSIG(status));
            }
        }    
    }
    return 0;
}

exec函数族

   extern char **environ;
    int execl(const char *path, const char *arg, .../* (char  *) NULL */);
    int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
    int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[], char *const envp[]);
    功能:
        利用当前的进程空间执行另外一份代码

    l:参数以列表形式传递
    v:参数以指针数组形式传递
    p:在系统目录下查找文件 
    e:传递环境变量

环境变量 

获取环境变量 

                extern char **environ;

/* 打印进程中所有环境变量的信息 */

        for(i=0;environ[i]!=0;i++)

        {

                printf("%s\n",environ[i]);

        }

(1) getenv

 getenv
    char *getenv(const char *name);
    功能:
        根据环境变量的名字获得环境变量对应的值
 

(2)setenv

setenv

        int setenv(const char *name, const char *value, int overwrite);
    功能:
        设置环境变量的值 

利用execvp函数和环境变量改变进程的执行代码,本代码由main.c转向执行hello.c,在系统路径下赵hello.c

hello.c

#include "../head.h"
int main(int argc, char const *argv[])
{
    int i=0;
    printf("helloPID:%d\n",getpid());//PID与main.c中的PID一致
    for(i=0;i<argc;i++)
    {
        printf("argv[%d]=%s\n",i,argv[i]);
    }
    return 0;
}

main.c 

#include "../head.h"
extern char **environ;
int main()
{
    int i=0;
    char *parg[10]={"hello","world","how","are","you",NULL};
    char curpath[4096]={0};
    char pathvalue[4096]={0};

     /* 打印进程中所有环境变量的信息 */
    for(i=0;environ[i]!=0;i++)
    {
        printf("%s\n",environ[i]);
    }
    /*获得环境变量中PATH的值*/
    printf("==============================\n");
    printf("PATH:%s\n",getenv("PATH"));
    printf("==============================\n");

    /**************************************
    **方法一:
    *      在PATH环境变量值原来的基础上加上当前目录
    ***************************************/
    getcwd(curpath,sizeof(curpath));//获取当前目录
    sprintf(pathvalue,"%s:%s",getenv("PATH"),curpath);//将当前目录加入pathvalue
    setenv("PATH",pathvalue,1);//重新设置为新的PATH
    printf("将PATH设定为:%s\n",pathvalue);
    printf("==============================\n");
    
    /*打印更新后PATH的值*/
    printf("PATH:%s\n",getenv("PATH"));
    printf("==============================\n");

    printf("exec上面\n");

    /* 利用该进程空间执行另外一份代码 */
   // execl("hello","hello","world","how","are","you",NULL);

   /* 在系统路径PATH下找hello文件名 */
   execvp("hello",parg);
   //方法二:也可以将hello文件拷贝到PATH中随便一个目录下
    printf("exec下面\n");
    return 0;
}

 system

 system函数原型:
    int system(const char *command);
    功能:
        在代码中执行一条shell命令

用进程知识实现system函数的功能

#include "../head.h"
int Mysystem(const char *pcommand)
{
    char ptmpbuff[1024]={0};
    strcpy(ptmpbuff,pcommand);
    char *parg[10]={0};
    int cnt=0;
  
    pid_t pid;
    /*解析命令*/
    parg[cnt]=strtok(ptmpbuff," ");
    if(parg[cnt]==NULL)
    {
        return -1;
    }
    cnt++;
   while(1)
   {
        parg[cnt]=strtok(NULL," ");
        if(parg[cnt]==NULL)
        {
            break;
        }
        cnt++;
    }
    /*创建子进程*/
    pid=fork();
    if(pid==-1)
    {
        return -1;
    }
    if(pid==0)
    {
        /*在/usr/bin路径下查找命令,并执行*/
        execvp(parg[0],parg);

    }
    else if(pid>0)
    {
        /*关闭进程*/
        wait(NULL);
    }
    return 0;
}
int main()
{
    printf("Mysystem上面\n");
    //system("ls -l");
    Mysystem("ls -l");
    printf("Mysystem下面\n");
}

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值