Linux启动新进程的三种方法

 更新时间:2017年04月27日 16:44:36   作者:迪米特  
本篇文章主要介绍了Linux启动新进程的三种方法以及这三种方法之间的区别。具有很好的参考价值。下面跟着小编一起来看下吧

程序中,我们有时需要启动一个新的进程,来完成其他的工作。

下面介绍了三种实现方法,以及这三种方法之间的区别。

1.system函数-调用shell进程,开启新进程

system函数,是通过启动shell进程,然后执行shell命令进程。

原型:

int system(const char *string);

string:shell命令字符串

返回值:成功返回命令退出码,无法启动shell,返回127错误码,其他错误,返回-1。

代码示例如下:

process_system.c

#include<stdlib.h>                               
#include<stdio.h>
int main()
{
    printf("Running ps with system\n");
    int code = system("ps au");//新进程结束后,system函数才返回
    //int code = system("ps au");//system函数立即返回
    printf("%d\n",code);
    printf("ps Done\n");
    exit(0);
}

输出结果:

system函数,在启动新进程时,必须先启动shell进程,因此使用system函数的效率不高。

2.exec系列函数-替换进程映像

exec系列函数调用时,启动新进程,替换掉当前进程。即程序不会再返回到原进程,

除非exec调用失败。

exec启动的新进程继承了原进程的许多特性,如在原进程中打开的文件描述符在新进程中仍保持打开。

需要注意的是,在原进程中打开的文件流在新进程中将关闭。原因在于,我们在前面讲过进程间通信的方式,进程之间需要管道才能通信。

原型:

int execl(const char *path,const char *arg0,...,(char*)0);
int execlp(const char *file,const char *arg0,...,(char*)0);
int execle(const char *path,const char *arg0,...,(char*)0,char *const envp[]);
int execv(cosnt char *path,char *const argv[]);
int execvp(cosnt char *file,char *const argv[]);
int execve(cosnt char *path,char *const argv[],char *const envp[]);

path/file:进程命令路径/进程命令名

argc:命令参数列表

envp:新进程的环境变量

代码示例如下:

process_exec.c

#include<stdio.h>
int main()
{
  printf("Running ps with execlp\n");
  execlp("ps","ps","au",(char*)0);
  printf("ps done");
  exit(0);
}

输出结果:

可以看出,调用execlp函数后,原进程被新进程替换,原进程中printf("ps done");没有被执行到。

3.fork函数-复制进程映像

1)fork函数的使用

fork和exec的替换不同,调用fork函数,可复制一个和父进程一模一样的子进程。

执行的代码也完全相同,但子进程有自己的数据空间,环境和文件描述符。

原型:

pid_t fork();

父进程执行时,返回子进程的PID

子进程执行时,返回0

代码示例如下:

process_fork.c

#include<stdio.h>
#include<sys/types.h>
int main()
{
  pid_t pid = fork();
  switch(pid)
  {
    case -1:
      perror("fork failed");
      exit(1);
      break;
    case 0:
      printf("\n");
      execlp("ps","ps","au",0);
      break;
    default:
      printf("parent,ps done\n");
      break;
  }
  exit(0);
}

输出结果:

调用fork函数后,新建了一个子进程,拷贝父进程的代码,数据等到子进程的内存空间。父进程和子进程执行互不影响。使用fork函数的返回值,来区分执行的是父进程,还是子进程。

2)僵尸进程

子进程退出后,内核会将子进程置为僵尸状态。此时,子进程只保留了最小的一些内核数据结构,如退出码,以便父进程查询子进程的退出状态。这时,子进程就是一个僵尸进程。

在父进程中调用wait或waitpid函数,查询子进程的退出状态,可以避免僵尸进程。

原型:

pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc,int options);

stat_loc:若不是空指针,则子进程的状态码会被写入该指针指向的位置。

pid:等待的子进程的进程号pid

options:标记阻塞或非阻塞模式

返回值:成功返回子进程的pid,若子进程没有结束或意外终止,返回0

wait:阻塞模式(使用了信号量),父进程调用wait时,会暂停执行,等待子进程的结束。

wait调用返回后,子进程会彻底销毁。

waitpid:与wait不同的是,

a.可以表示四种不同的子进程类型

     pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同

  pid >0 等待进程ID与pid值相同的子进程

  pid==0 等待与调用者进程组ID相同的任意子进程

  pid<-1 等待进程组ID与pid绝对值相等的任意子进程

b.当options的值为WNOHANG时,为非阻塞模式,即waitpid会立即返回

此时,可以循环查询子进程的状态,若子进程未结束,waitpid返回,做其他工作。

这样提高了程序的效率。

wait函数使用示例如下:

process_fork3.c

#include<wait.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
  pid_t pid = fork();
  int stat = 0;
  switch(pid)
  {
    case -1:
      perror("fork failed");
      exit(1);
      break;
    case 0:
      printf("\n");
      exit(0);
      break;
    default:
      pid = wait(&stat);
      printf("Child has finished:PID=%d\n",pid);
      printf("parent,ps done\n");
      break;
  }
  exit(0);
}

输出结果:

waitpid函数使用示例如下:

process_fork2.c

#include<wait.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
  pid_t pid = fork();
  int stat = 0;
  switch(pid)
  {
    case -1:
      perror("fork failed");
      exit(1);
      break;
    case 0:
      printf("\n");
      execlp("ps","ps","au",0);
      break;
    default:
      do
      {
        pid = waitpid(pid,&stat,WNOHANG);
        if(pid==0)
        {
          printf("parent do something else.\n");
          sleep(1);
        }
      }while(pid==0);
      printf("Child has finished:PID=%d\n",pid);
      printf("parent,ps done\n");
      break;
  }
  exit(0);
}

输出结果:

4.启动新进程三种方法的比较

1)system函数最简单,启动shell进程,并在shell进程中执行新的进程。

效率不高,system函数必须等待子进程返回才能接着执行。

2)exec系列函数用新进程替换掉原进程,但不会返回到原进程,除非调用失败。

该函数继承了许多原进程的特性,效率也较高。

3)fork函数,复制一个子进程,和父进程一模一样,但是拥有自己的内存空间。父子进程执行互不影响。需要注意僵尸子进程的问题。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

相关文章

  • Shell获取字符串长度的多种方法总结

    Shell获取字符串长度的多种方法总结

    这篇文章主要给大家总结分享了关于Shell获取字符串长度的多种方法,包括了利用${#str}、利用awk的length方法、利用awk的NF项、利用wc的-L参数、利用expr的length方法以及利用expr的$str : ".*"技巧来实现方法示例,需要的朋友可以参考下。
    2017-08-08
  • Bash脚本中Sleep命令的使用

    Bash脚本中Sleep命令的使用

    本文主要介绍了Bash脚本中Sleep命令的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • shell随机定时修改密码change_passwd.sh

    shell随机定时修改密码change_passwd.sh

    这篇文章主要介绍了shell随机定时修改密码change_passwd.sh,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-12-12
  • 消耗CPU资源的shell脚本

    消耗CPU资源的shell脚本

    使用死循环消耗CPU资源,如果服务器是有多颗CPU,可以选择消耗多少颗CPU的资源
    2013-08-08
  • Shell脚本对文件中的行、单词、字符进行迭代输出示例

    Shell脚本对文件中的行、单词、字符进行迭代输出示例

    这篇文章主要介绍了Shell脚本对文件中的行、单词、字符进行迭代输出示例,本文讲解了迭代文件中的每一行、迭代一行中的每一个单词、迭代一个单词中的每一个字符等内容,需要的朋友可以参考下
    2014-11-11
  • Linux 下sudo网络权限详解

    Linux 下sudo网络权限详解

    这篇文章主要介绍了Linux 下sudo网络权限详解的相关资料,需要的朋友可以参考下
    2017-05-05
  • shell脚本实战-while循环语句

    shell脚本实战-while循环语句

    这篇文章主要介绍了shell脚本实战-while循环语句,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • shell脚本 自动创建用户详解及实例代码

    shell脚本 自动创建用户详解及实例代码

    这篇文章主要介绍了shell脚本 自动创建用户详解及实例代码的相关资料,需要的朋友可以参考下
    2017-03-03
  • Linux 按时间批量删除文件命令(删除N天前文件)

    Linux 按时间批量删除文件命令(删除N天前文件)

    这篇文章主要介绍了Linux 按时间批量删除文件的命令写法(删除N天前文件),需要的朋友可以参考下
    2017-05-05
  • shell 提取文件名和目录名的方法实现

    shell 提取文件名和目录名的方法实现

    本文主要介绍了shell 提取文件名和目录名的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02

最新评论