linux知识点----进程控制

更多知识点:linux目录索引


进程创建———fork()

函数: pid_t fork(void)

说明:

  • 父进程创建子进程后,父子进程交替运行

  • 运行过程中,子进程死亡,父进程存在,子进程称为僵尸进程,且僵尸进程不可人为杀死,必须等到父进程结束后,才会将其回收

  • 运行过程中,父进程死亡,子进程存在,子进程称为孤儿进程,由1号进程托管,死亡后由1号进程负责回收

  • fork()函数返回值有两个,父进程返回子进程的id,子进程返回0,出错返回-1;关于父子进程返回值的问题可点链接关于fork()父子进程返回值的问题


僵尸进程的创建
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main()
{
    pid_t pid = fork();//创建进程,返回值-1,0,大于0
    int i = 0;
    if(pid == 0)//子进程
    {
        while(i < 5)
        {
            printf("子进程运行%d \n",i);
            sleep(1);
            ++i;
        }
        exit(0);//子进程运行5秒退出,成为僵尸进程

    }
    while( i < 15)
    {
        printf("父进程运行%d \n",i);
        sleep(1);
        ++i;
    }
    return 0;
}

效果:

这里写图片描述


孤儿进程的创建
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main()
{
    pid_t pid = fork();//创建进程,返回值-1,0,大于0
    int i = 0;
    if(pid == 0)//子进程
    {
        while(i < 15)
        {
            printf("子进程运行%d \n",i);
            sleep(1);
            ++i;
        }
        exit(0);
    }

    while( i < 5) //父进程运行5秒退出,子进程为孤儿进程,被1号进程托管
    {
        printf("父进程运行%d \n",i);
        sleep(1);
        ++i;
    }
    return 0;
}

效果:

这里写图片描述


进程创建————vfork()

函数: pid_t vfork()

说明:

  • vfork创建后,父进程被阻塞,直到子进程运行完毕后,父进程才开始运行

  • 父进程和子进程共享代码段,数据段(一切内存空间)

  • vfork创建进程,就算写时,也不进行拷贝

  • vfork创建的进程,必须使用exit 或者exec 结束进程

一个例子:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    pid_t pid = vfork();
    int i=0;
    if(pid==0)//子进程运行5秒
    {
        for(;i<5;++i)
        {
            printf("子进程运行:%d\n",i);
            sleep(1);
        }
            exit(0);
    }

    for(i=0;i<5;++i)
    {
        printf("父进程运行:%d\n",i);
        sleep(1);

    }
    return 0;
}

结果:

这里写图片描述


进程等待

函数: pid_t wait( int* status );

说明:

  • 当子进程死亡,变为僵尸进程,父进程调用wait 回收僵尸进程

  • 回收成功,返回值为被回收的子进程id,失败返回-1

  • 如果一个父进程有多个子进程,其中一个子进程死亡,(成为僵尸进程),wait进行返回

  • 调用一次wait只能回收一个子进程

例子:多进程同时运行
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
   pid_t pid = fork();

   if(pid == 0)//创建子进程
   {
       if(fork() > 0) //子进程创建孙子进程后退出
       {
           exit(0);
       }
       while(1)//孙子进程一直运行
       {
           printf("孙子进程\n");
           sleep(1);
       }
   }
   else
   {
       wait(NULL);//父进程等待回收死亡的子进程(僵尸进程)

       while(1)
       {
           printf("爷爷\n");//父进程一直运行
           sleep(1);
       }
   }
    return 0;
}
//这段代码实现的功能是:
//父进程创建子进程,子进程创建孙子进程
//杀死子进程,父进程中的wait函数处理掉僵尸进程
//父进程和孙子进程仍存活,并发运行,只不过此时孙子进程属于孤儿进程

效果:

这里写图片描述

函数: pid_t waitpid(pid_t,int* status,int options)

函数说明:

  • waitpid()会暂时停止目前进程的执行, 直到有信号来到或子进程结束

  • 如果在调用waitpid()时子进程已经结束, 则waitpid()会立即返回子进程结束状态值.

  • 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一块返回. 如果不在意结束状态值, 则参数status 可以设成NULL.。

  • 如果执行成功则返回子进程识别码(PID), 如果有错误发生则返回-1. 失败原因存于errno 中.

  • 参数pid 为欲等待的子进程识别码, 其他数值意义如下:

    pid<-1 等待进程组识别码为pid 绝对值的任何子进程.
    pid=-1 等待任何子进程, 相当于wait().
    pid=0 等待进程组识别码与目前进程相同的任何子进程.
    pid>0 等待任何子进程识别码为pid 的子进程.
  • 参数option 可以为0 或下面的OR 组合:

WNOHANG:如果pid指定的子进程没有结束,则waitpid()返回0,不予等待,若正常结束,返回该子进程的id。

WUNTRACED:如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会.。子进程的结束状态返回后存于status, 底下有几个宏可判别结束情况

WIFEXITED(status):如果子进程正常结束则为非真。

WEXITSTATUS(status):取得子进程exit()返回的结束代码, 一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。

WIFSIGNALED(status):如果子进程是因为信号而结束则此宏值为真。

WTERMSIG(status):取得子进程因信号而中止的信号代码, 一般会先用

WIFSIGNALED 来判断后才使用此宏.。

WIFSTOPPED(status):如果子进程处于暂停执行情况则此宏值为真. 一般只有使用

WUNTRACED时才会有此情况。

WSTOPSIG(status):取得引发子进程暂停的信号代码, 一般会先用WIFSTOPPED 来判断后才使用此宏。


进程终止

销毁进程的过程:

  1. 释放资源、文件等

  2. 记录运行过的信息(例如:哪些程序曾经运行过)

  3. 将进程的状态置为僵尸状态

  4. 转存储调度:将CPU让给所要使用的进程

进程终止的方法

正常退出:

  • main 函数退出

  • 调用exit

  • 调用_exit

  • 执行退出处理函数

  • 刷新缓存

异常退出

  • Ctrl+c

  • about

两个函数

_exit 函数

void  _exit(int status);
参数:status定义了进程的终止状态,父进程通过wait来获取该值

注意:虽然status是int,但是仅有低8位可以被父进程所用。所以exit(-1)时,在终端执行$?发现返回值为255

exit函数

void exit(int status);

二者区别:

  • _exit()函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;

  • exit() 函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,也是因为这个原因,有些人认为exit已经不能算是纯粹的系统调用。

  • exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是”清理I/O缓冲”。


fork、execvp、popen和system的比较

fork创建子进程,以及对写时拷贝的解释

fork() 一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段:

  • 首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。

  • 现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。

  • 事实上,目前大多数的unix系统在实现上并没有作真正的copy。一般的,CPU都是以“页”为单位分配空间的,象INTEL的CPU,其一页在通常情况下是4K字节大小,而无论是数据段还是堆栈段都是由许多“页”构成的,fork函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区 别,系统就将有区别的“页”从物理上也分开。系统在空间上的开销就可以达到最小。

    exec修改子进程

      
      对于exec系列函数 一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。

     不过exec类函数中有的还允许继承环境变量之类的信息,这个通过exec系列函数中的一部分函数的参数可以得到。

popen

  
  对于popen函数,他会通过command参数重新启动shell命令,并建立两个进程间的管道通信.
详见:popen函数_Linux C 中文函数手册

system

 对于system函数,它也会重新启动shell命令,当执行完毕后,程序会继续system下一行代码执行.
详见:system函数_Linux C 中文函数手册

注:以上内容摘自:popen system fork exec等函数的区别

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值