关于进程的一些基础知识

351 阅读4分钟
  • 进程的概念

    程序

      > 存放在磁盘上的指令和数据的有序集合(文件);
    
      > 静态的;
    

    进程

      > 执行一个程序所分配的资源的总称;
    
      > 进程是程序的一次执行过程;
    
      > 动态的,包括创建、调度、执行和消亡;
    
  • 进程的内容

    代码用户数据段系统数据段

    系统数据段包括了进程控制块、CPU寄存器的值、堆栈;

    进程控制块(pcb)

      > 进程标识PID;
    
      >  进程用户的信息;
    
      > 进程状态、优先级;
    
      > 文件描述符表(记录了系统中打开的所有的文件的信息);
    

    CPU寄存器

    program counter (存放的是下一条指令的地址);

    保存了所有的局部变量;

  • 进程的类型

    交互进程

    在shell下启动。可以在前台运行,也可以是在后台运行;

    批处理进程

    和在终端无关,被提交到一个作业队列中,被作业管理工具顺序执行(开发时较少使用);

    守护进程

    和终端无关,一直在后台运行(生命期很长);

  • 进程状态

    运行态

    程序正在运行,或者准备运行;

    等待态

    程序在等待一个事件的发生或者某种系统资源(可中断和不可中断);

    停止态

    进程被终止,收到信号后可继续运行;

    死亡态

    已终止的进程,但pcb没有被释放;

  • 一些相关命令

    ps -ef显示当前时刻系统中所有进程的信息;ps -ef|grep <>筛选显示

    ps aux|grep <>-ef多显示进程的当前状态;

    top 查看进程的动态信息;

    /proc 进入该目录下查看进程的详细信息;

    nice -n <NI> ./<001> 按用户指定的优先级运行进程(默认的 nice 值是 0 ,区间为 -20 到 19),nice值越小,优先级越高;

    renice -n <NI> <PID> 改变正在运行进程的优先级;

    jobs 查看一个后台进程;

    fg <PID> 把一个后台进程放到前台;

    bg <PID> 让处于停止态的后台进程运行起来;

  • 创建子进程

  • fork函数

    头文件:

    #include <unistd.h>

    函数原型:

    pid_t fork(void);

    返回值:

    创建新的进程,失败时返回-1;

    fork()被调用一次会返回两次,成功时父进程返回子进程的进程号,子进程返回 0

    通过 fork 的返回值区分父进程和子进程;

    示例:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,const char *argv[])
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork");
        return -1;
    }
    else if (pid == 0) {
        printf("child process: my pid is %d\n",getpid());
    }
    else{
        printf("parent process: my pid is %d\n",getpid());
    }
    return 0;
}
  • 父子进程

    子进程继承了父进程的内容;

    父子进程有独立的地址空间,互不影响;

    若父进程先结束,子进程成为孤儿进程,会被init进程收养,同时变为后台进程;

    若子进程先结束,父进程没有及时回收,子进程则变为僵尸进程(pcb未被释放);

    父子进程的运行顺序不确定,取决于内核的调度;

  • 结束进程

  • exit / _exit函数

    头文件:

    #include <stdlib.h> #include <unistd.h>

    函数原型:

    void exit(int status);

    void _exit(int status);

    返回值;

    结束当前进程并将status返回;

    exit 结束进程时会刷新流的缓冲区;

    示例:

    #include <stdio.h>
                  /*exit()的头文件*/
    #include <stdlib.h>
                 /*_exit()的头文件*/
    #include <unistd.h>
    int main(int argc,const char *argv[])
    {
        printf("success");
            /*换成_exit(0)则屏幕无输出*/
        exit(0);
        printf("success too");
        return 0;
    }
    
  • exec函数族

    进程的当前内容被替换,实现父子进程执行不同的程序;

  • execl / execlp 函数

    头文件: #include <unistd.h>

    函数原型:

    int execl(const char *path, const char *arg, ...);

    int execlp(const char *file, const char *arc, ...);

    path ,执行程序的名称和路径(绝对路径或相对路径);

    arg... 传递给执行程序的参数列表;

    file 执行的程序的名称,在PATH(shell的环境变量)中查找;

    返回值:

    成功执行指定的程序,失败则返回EOF;

    示例:(执行ls命令,显示/etc目录下所有的文件详细信息)

    if(execl("/bin/ls","ls","-a","-l","/etc"NULL) < 0) {
       perror("exec error");
    }
    
    if(execlp("ls","-a","-l","/etc",NULL) < 0) {
        perror("execlp error");
    }
    
  • execv / execvp 函数

    头文件:

    #include <unistd.h>

    函数原型:

    int execv(const char *path, char *const argv[]);

    int sescvp(const char *file, char *const argv[]);

    相较于execl()和execlp(),参数列表封装成字符指针数组;

    返回值:

    成功执行指定的程序,失败则返回EOF;

    示例:(功能同上)

            /*定义一个字符指针数组*/
    char *const buf[] = {"ls","-a","-l","/etc",NULL};
    if(execv("/bin/ls",buf) < 0) {
        perror("execv");
    }
    
  • system 函数

    属于比较简单的方法,不手动创建子进程;

    头文件:

    #incude <stdlib.h>

    函数原型:

    int system(const char *command);

    在当前进程中会自己创建子进程;

    返回值:

    成功时返回命令 command 的返回值,失败时返回EOF;

    当前进程等待 command 执行结束之后才继续执行;

  • 进程回收

  • wait 函数

  • waitpid 函数