Linux下的系统编程——进程的执行与回收(八)

简介: Linux下的系统编程——进程的执行与回收(八)

一、exec函数

将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变换核丕换壳

 

1.execlp:

   int execlp(const char file, const char arg, ...);       借助 PATH 环境变量找寻待执行程序

       参1: 程序名

       参2: argv0

       参3: argv1

       ...: argvN

       哨兵:NULL

该函数通常用来调用系统程序。如: ls、date、cp、cat等命令。

 

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid;        //创建子进程
 
  if(pid == -1){
    perror("fork error");
    exit(1);
  }else if(pid == 0){        //子进程
    //execlp("ls","-l","-d","-h",NULL);//错误写法
 
         /************************************/
     execlp("ls","ls","-l","-h",NULL);    
         /************************************/
 
    perror("exec error");
    exit(1);
  }
  else if(pid > 0){        //父进程
    sleep(1);
    printf("I'm parent : %d\n",getpid());
  }
  return 0;
}

date命令的实现:

execlp("date","date",NULL);

 2.execl:

int execl(const char path, const char arg, ...);    自己指定待执行程序路径。(路径+程序名)

#include <stdio.h>
 
 
int main(int argc, char **argv)
{
  
  printf("Hello, %s!\n", argv[1]);
  
  printf("Hello, world!\n");
  
  return 0;
}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid;     //创建子进程
 
  if(pid == -1){
    perror("fork error");
    exit(1);
  }else if(pid == 0){    //子进程
    //execlp("ls","-l","-d","-h",NULL);
    //execlp("date","date",NULL);
 
         /************************************/
     execl("./a.out","./a.out","linux",NULL);
         /************************************/
 
    perror("exec error");
    exit(1);
  }
  else if(pid > 0){    //父进程
    sleep(1);
    printf("I'm parent : %d\n",getpid());
  }
  return 0;
}

3.execvp

加载一个进程,使用自定义环境变量env

int execvp(const charfile, const char argv[]);

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid;            创建子进程
 
  if(pid == -1){
    perror("fork error");
    exit(1);
  }else if(pid == 0){        //子进程
    //execlp("ls","-l","-d","-h",NULL);
    //execlp("date","date",NULL);
    //execl("./a.out","./a.out","linux",NULL);
    
        /************************************/
    char *argv[] = {"date",NULL};
    execvp("date",argv);
        /************************************/
 
    perror("exec error");
    exit(1);
  }
  else if(pid > 0){         //父进程
    sleep(1);
    printf("I'm parent : %d\n",getpid());
  }
  return 0;
}

4.exec函数族的一般规律:

       l:命令行参数列表

       p:使用PATH环境变量

       v:使用命令行参数数组

       exec函数一旦调试成功即执行新的程序,不返回。只要失败才返回,错误值-1。所以通常我们直接在exec函数调用后调用 perror()和exit()。无需if判断。·

二、回收子进程

1.孤儿进程:

父进程死亡子进程进孤儿院

       孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

模拟孤儿进程:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(void)
{
    pid_t pid;
    pid = fork();
 
    if (pid == 0) {
        while (1) {
            printf("I am child, my parent pid = %d\n", getppid());
            sleep(1);
        }
    } else if (pid > 0) {
        printf("I am parent, my pid is = %d\n", getpid());
        sleep(9);
        printf("------------parent going to die------------\n");
    } else {
        perror("fork");
        return 1;
    }
 
    return 0;
}

查看进程状态:ps ajx

进程孤儿院:

1   2035   2035   2035 ?            -1 Ss    1001   0:00 /lib/systemd/systemd --user

解决方法:

                杀死子进程:     kill -9 4871

2 .僵尸进程:

子进程死亡,父进程一直不管

       僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(zombie)进程。(死亡以后没有回收)

       特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

模拟僵尸进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(void)
{
    pid_t pid;
    pid = fork();
 
    if (pid == 0) {
        printf("---child, my parent= %d, going to sleep 10s\n", getppid());
        sleep(10);
        printf("-------------child die--------------\n");
    } else if (pid > 0) {
        while (1) {
            printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);
            sleep(1);
        }
    } else {
        perror("fork");
        return 1;
    }
 
    return 0;
}

查看进程状态:ps ajx

解决方法:

       杀死父进程:   kill -9 4770

3.wait:

   wait函数:    回收子进程退出资源, 阻塞回收任意一个。

  pid_t wait(int status)

   

   参数:(传出) 回收进程的状态。

   返回值:成功: 回收进程的pid

                  失败: -1, errno

   函数作用1:    阻塞等待子进程退出

   函数作用2:   清理子进程残留在内核的 pcb 资源

   函数作用3:    通过传出参数,得到子进程结束状态

   

   获取子进程正常终止值

       WIFEXITED(status) --》 为真 --》调用 WEXITSTATUS(status) --》 得到 子进程 退出值。

   获取导致子进程异常终止信号

      WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常终止的信号编号。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(void)
{
    pid_t pid, wpid;
    int status;
 
    pid = fork();
    if (pid == 0) {
        printf("---child, my id= %d, going to sleep 10s\n", getpid());
        sleep(10);
        printf("-------------child die--------------\n");
        return 73;
    } else if (pid > 0) {
        //wpid = wait(NULL);          // 不关心子进程结束原因
        wpid = wait(&status);       // 如果子进程未终止,父进程阻塞在这个函数上
        if (wpid == -1) {
            perror("wait error");
            exit(1);
        }
        if (WIFEXITED(status)) {        //为真,说明子进程正常终止. 
            printf("child exit with %d\n", WEXITSTATUS(status));
 
        }
        if (WIFSIGNALED(status)) {      //为真,说明子进程是被信号终止.
 
            printf("child kill with signal %d\n", WTERMSIG(status));
        }
 
        printf("------------parent wait finish: %d\n", wpid);
    } else {
        perror("fork");
        return 1;
    }
 
    return 0;
}

正常终止:

被信号终止:

4.waitpid

waitpid函数:    指定某一个进程进行回收。可以设置非阻塞。          

   waitpid(-1, &status, 0) == wait(&status);

  pid_t waitpid(pid_t pid, int status, int options)

   

参数:

       pid:指定回收某一个子进程pid

           > 0: 待回收的子进程pid

           -1:任意子进程

           0:同组的子进程。

       status:(传出) 回收进程的状态。

       options:WNOHANG 指定回收方式为,非阻塞。

 

返回值:

       > 0 : 表成功回收的子进程 pid

       0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。

       -1: 失败。errno

回收任意子进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid,wpid;
  for(i = 0;i < 5;i++){
    if(fork()==0)       //循环期间,子进程不fork
      break;
  }
  if(i == 5){           //父进程
        
        //wait(NULL);//一次wait/waitpid函数调用,只能回收一个子进程
        /*****************************************/
    wpid = waitpid(-1,NULL,WNOHANG);//回收任意子进程,没有结束的子进程,父进程直接返回0
        /****************************************/
 
    if(wpid == -1)
    {
      perror("waitpid error");
      exit(1);
    }
    printf("I'm parent ,wait a child finish :%d\n",wpid);
  }else{            //子进程,从break跳出
    sleep(i);
    printf("I'm %dth child\n",i+1);
  }
  return 0;
}

回收指定进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid,wpid,tmpid;
  for(i = 0;i < 5;i++){
    pid = fork();
    if(pid == 0){       //循环期间,子进程不fork
      break;
    }
        if(i == 2){
            tmpid = pid;
      printf("*************pid= %d***************\n",pid);
    }
  }
  if(i == 5){           //父进程,从表达式2跳出
        sleep(5);     //设置睡眠,等所有子进程结束后再回收
 
        //wait(NULL);    //一次wait/waitpid函数调用,只能回收一个子进程
        //wpid = waitpid(-1,NULL,WNOHANG);     //回收任意子进程,没有结束的子进程,父进程直接返回0
    printf("I am parent , before waitpid , pid = %d\n",tmpid);
 
        /********将前面sleep(5)屏蔽***************/
    //wpid = waitpid(tmpid,NULL,0);         //指定一个进程回收,阻塞回收
        /****************************************/
        
        /*****************************************/
    wpid = waitpid(tmpid,NULL,WNOHANG);     //指定一个进程回收,不阻塞
        /****************************************/
 
    if(wpid == -1)
    {
      perror("waitpid error");
      exit(1);
    }
    printf("I'm parent ,wait a child finish :%d\n",wpid); //wpid回收的是真正的子进程id
  }else{            //子进程,从break跳出
    sleep(i);
    printf("I'm %dth child,pid = %d\n",i+1,getpid());
  }
  return 0;
}

注意:

       一次wait/waitpid调用只能回收一个子进程,无法回收他孙子辈的进程,多次清理需要while

5.waitpid回收多个子进程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid,wpid;
  for(i = 0;i < 5;i++){
    pid = fork();
    if(pid == 0){       //循环期间,子进程不fork
      break;
    }
  }
  if(i == 5){    //父进程
    /**********使用阻塞回收子进程********/
 
    while((wpid = waitpid(-1,NULL,0))){
      printf("wait child %d\n",wpid);
    }
 
    /***********************************/
  }else{        //子进程
    sleep(i);
    printf("I'm %dth child ,pid =%d\n",i+1,getpid());
  }
 
    return 0;
}

结束一个回收一个

之后返回-1,表示没有失败了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
 
int main(int argc,char *argv[])
{
  int i;
  pid_t pid,wpid;
  for(i = 0;i < 5;i++){
    pid = fork();
    if(pid == 0){       //循环期间,子进程不fork
      break;
    }
  }
  if(i == 5){
    /*********使用阻塞回收子进程***********/
    /*
    while((wpid = waitpid(-1,NULL,0))){
      printf("wait child %d\n",wpid);
    }
    */
    /***********************************/
    
        /*******使用非阻塞方式回收子进程******/
 
    while((wpid = waitpid(-1,NULL,WNOHANG)) != -1){
      if(wpid > 0){ 
        printf("wait child %d\n",wpid);
      }else if(wpid == 0){
        sleep(1);
        continue;
      }
        /************************************/
 
    }
 
  }else{
    sleep(i);
    printf("I'm %dth child ,pid =%d\n",i+1,getpid());
  }
 
    return 0;
}


目录
相关文章
|
6天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
25 3
|
6天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
19 2
|
6天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
24 3
|
9天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
26 6
|
9天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
35 6
|
10天前
|
机器学习/深度学习 自然语言处理 Linux
Linux 中的机器学习:Whisper——自动语音识别系统
本文介绍了先进的自动语音识别系统 Whisper 在 Linux 环境中的应用。Whisper 基于深度学习和神经网络技术,支持多语言识别,具有高准确性和实时处理能力。文章详细讲解了在 Linux 中安装、配置和使用 Whisper 的步骤,以及其在语音助手、语音识别软件等领域的应用场景。
40 5
|
10天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
167 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。