进程 exec

18 篇文章 0 订阅

【1】进程

  • 进程是程序的一次执行过程

    1. 程序和进程之间的区别:

      1. 程序
        程序是保存在磁盘上的可执行的二进制文件
        是静态、是指令的集合,没有执行的过程。
      2. 进程:一个独立的可调度的任务
        执行一个程序所分配的资源的总称
        进程是程序的一次执行过程
        进程是动态的,包括创建、调度(时间片)、执行和消亡
    2. 进程包含三个段:

      1. “数据段”存放的是全局变量、常数,static修饰的变量等。
      2. “正文段”存放的是程序中的代码
      3. “堆栈段”存放的是函数的返回地址、函数的参数以
        及程序中的局部变量,malloc开辟的内存空间。
        产生一个进程会分配0-4G的虚拟内存空间:
        0-3G:用户私有空间
        3-4G: 所有进程公用的空间
    3. 进程内存管理

      1. 每个进程都分配4G虚拟内存空间

      2. 0G~3G 是用户空间, 3G~4G 是内核空间

      3. 3G~4G 是所有进程共享的
        (mmu:内存管理单元,虚拟机只有mmu能操作物理地址,管理用户
        使用的物理地址其他用户将不能在获取使用)
        代码段、数据段、bss段、只读数据段、
        堆段、栈段(都占用自己的虚拟地址并映射相应的物理地址)

      4. 进程是程序执行和资源管理的最小单位。
        包含的的资源有:物理内存、文件描述符、虚拟地址 0G~4G、
        CPU时间片、进程号(PID唯一标识一个进程)

    4. 进程的类型:

      1. 交互进程:交互进程既可以在前台运行,也可以在后台运行。
        该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户
        的输入后,该类进程会立刻响应。
      2. 批处理进程:该类进程不属于某个终端,
        它被提交到一个队列中以便顺序执行
      3. 守护进程:该类进程在后台运行。它一般在Linux启动时
        开始执行,系统关闭时才结束。
    5. 进程的运行状态:

      1. 运行态:此时进程或者正在运行,或者准备运行。
      2. 等待态:此时进程在等待一个事件的发生或某种系统资源。
        可中断:处在这种状态下的进程可以被信号中断,
        接收到信号或被显示地唤醒呼叫,唤醒之后,
        进程将转变为运行态。
        不可中断:它不会处理信号,只有在它所等待的
        事件发生时,进程才被显示的唤醒
      3. 停止态:此时进程被中止。
      4. 死亡态:这是一个已终止的进程,但还在进程向量
        数组中占有一个task_struct结构。

    D uninterruptible sleep (usually IO)(不可中断的等待态)
    R running or runnable (on run queue)(运行态)
    S interruptible sleep (可中断的等待态)
    T stopped, either by a job control signal or because it is
    being traced.(停止态)
    X dead (should never be seen)(死亡态)
    Z defunct (“zombie”) process, terminated but not reaped by its
    parent.(僵尸态)

    For BSD formats and when the stat keyword is used, additional
    characters may be displayed:
    < high-priority (not nice to other users)(高优先级)
    N low-priority (nice to other users)(低优先级)
    s is a session leader(会话组组长)
    多个进程可以组成一个组,多个组可以组成一个会话。
    多个会话可以组成一个会话组。
    l is multi-threaded (线程)

    • is in the foreground process group.(前台)
      空:表示后台

【2】进程的相关命令

ps:
ps -aux 查看进程信息
ps -ef 父进程pid
ps -ajx 查看进程组id 会话id 父进程pid

top

nice sudo nice -5 ./a.out
NI 值越小,优先级越高 -20~19
PR NI+20
renice
sudo renice -3 pid

kill:
kill -l :查看信号
kill -9 pid

前后进程切换的命令:
fg:将后台进程调到前台
bg:后台暂停的进程在后台运行起来

jobs:查看后台有那些作业(任务)
[1]pid runing ./a.out &

fg %1

【3】创建进程

  • fork
    #include <unistd.h>
    pid_t fork(void);
    功能:创建一个子进程
    参数:无
    返回值:
    失败:-1
    成功:父进程中返回的是子进程的pid >0
    子进程中返回的是 0 =0

    1. 一旦执行fork函数,进程就变成了两个进程,子进程拷贝父进程的代码段
      正文段、堆栈段。子进程几乎拷贝了父进程的所有内容,除去父进pid、task_struct 等。

      fork创建的子进程和父进程之间是相互独立,都有属于自己的0-4G虚拟内存
      空间。一旦fork产生子进程之后,子进程和父进程之间的操作相互独立。

    2. fork之前的代码会被复制,但是不会被执行,之后的代码会被复制一份并执行。

    3. 如果父进先退出,子进程会变成孤儿进程,被init进程收养,并且成为后台进程。

    4. 如果子进程先退出,父进程没有给子进程回收资源,子进程就会变成僵尸进程,占用内存资源

    5. 验证:fork之前打开的文件。fork之后父子进程拿到同一个文件描述符,那是否是操作的是同一个
      文件指针?(文件只被打开一次,只有一个文件指针) 是

补充:写时拷贝
由于之前的fork完整地拷贝了父进程的整个地址空间,
因此执行速度是比较慢的。为了提高效率,Unix系统
设计者创建了现代unix版的fork。现代unix版的fork
也创建新进程,但不产生父进程的副本。它通过允许
父子进程可访问相同物理内存从而伪装了对进程地址
空间的真实拷贝,当子进程需要改变内存中数据时才
拷贝父进程。这就是著名的"写操作时拷贝"
(copy-on-write)技术

【4】退出进程的函数exit/_exit

  1. #include <stdlib.h>
    void exit(int status);
    功能:退出进程
    参数:status:退出进程的状态
    返回值:无

  2. #include <unistd.h>
    void _exit(int status);
    功能:退出进程
    参数:status:退出进程的状态
    返回值:无
    exit退出进程会刷新缓存区,_exit退出进程不刷新缓存区

【5】获取进程pid和父进程pid的函数

  • getpid/getppid
    #include <sys/types.h>
    #include <unistd.h>
    pid_t getpid(void);
    返回进程的PID
    pid_t getppid(void);
    返回父进程的PID
 注意:这两个函数是永远不会失败的函数。

【6】回收子进程资源

  1. wait
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    功能:阻塞等待子进程退出回收子进程资源
    参数:status:保存子进程的退出状态
    返回值:
    成功:退出进程的进程号
    失败:-1

    ? status若为空,表示忽略子进程退出时的状态
    ? status若不为空,表示保存子进程退出时的状态
    另外,子进程的结束状态可由Linux中一些特定的宏来测定。
    返回值:成功:子进程的进程号
    失败:-1

    WIFEXITED(*status) 判断子进程是否正常结束//wifexited
    //正常结束返回1,非正常结束返回0。
    WEXITSTATUS(*status) 获取子进程返回值//wexitstatus
    //拿到的是一个8位的数,占一个字节,
    //如:exit(10)为10,exit(-10),打印的结果为246
    //若return的值,只是高八位表示。

  2. waitpid
    pid_t waitpid(pid_t pid, int *status, int options);
    功能:阻塞等待子进程退出,回收资源
    参数:pid:>0 指定子进程进程号
    =-1任意子进程
    =0 等待其组ID等于调用进程的组ID的任一子进程
    <-1等待其组ID等于pid的绝对值的任一子进程
    status:同上
    optionsWNOHANG:不阻塞
    0:阻塞
    返回值:正常:结束的子进程的进程号
    使用选项WNOHANG且没有子进程结束时:0
    出错:-1
    waitpid(-1,NULL,0) ==> wait(NULL);

【7】exec族函数

  1. 概念:
    exec函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的
    文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。
    在执行完之后,原调用进程的内容除了进程号外,其他全部都被替换了。

  2. 何时使用exec?

    1. 当进程认为自己不能再为系统和用户做出任何贡献了时就可以调用exec函数,让自己执行新的程序
    2. 如果某个进程想同时执行另一个程序,它就可以调用fork函数创建子进程,然后在子进程中调用任何
      一个exec函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样
   int execl(const char *path, const char *arg, ...);
   int execv(const char *path, char *const argv[]);
   
   int execlp(const char *file, const char *arg, ...);
   int execvp(const char *file, char *const argv[]);
   
   int execle(const char *path, const char *arg,..., char * const envp[]);
   int execvpe(const char *file, char *const argv[],char *const envp[]);
#if 0
        l : list列表,参数要以列表形式,展现出来。
		p : path路径,从系统环境变量中去找可执行文件
		v : 向量数组,将列表的参数装到数组中去。
		e :  函数传递指定参数envp,允许改变子进程的环境,无后缀e时,
		      子进程使用当前程序的环境。envp也是一个以NULL结尾的字符串数组指针
#endif
  1. 使用区别:
    1. 可执行文件查找方式
      无p:指定完整的文件目录路径,
      有p:只给出文件名,系统会自动从环境变量“$PATH”所包含的路径中进行查找。
    2. 参数表传递方式
      有l:表示逐个列举的方式;
      有v:表示将所有参数构造成指针数组传递,其语法为char *const argv[]
    3. 环境变量的使用
      exec函数族可以默认使用系统的环境变量,也可以传入指定的环境变量。
      这里,以“e”(Enviromen)结尾的两个函数execle、execve就可以在envp[]中传递
      当前进程所使用的环境变量,一般为NULL即可

【8】守护进程的创建

  1. 创建一个子进程,让父进程退出。
    目的:子进程变成后台进程
    fork();

  2. 创建一个新的会话,将进程设置为会话组组长
    目的:让进程脱离终端
    setsid();

  3. 修改路径为根目录。
    目的:防止进程执行的路径被删除
    chdir();

  4. 重设权限掩码。
    umask();

  5. 关闭文件描述符。
    close();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值