linux进程控制(进程创建,进程退出,进程等待,进程替换)

本文详细介绍了进程管理中的关键概念和技术,包括进程创建、进程退出、进程等待及进程替换等内容。通过具体的函数介绍和示例代码,帮助读者理解这些概念并掌握实际操作。

1.进程创建

简单讲一下进程创建主要会用到fork()函数来创建进程;
pid_t fork(void); 头文件:<unistd.h>
特点:父子进程代码共享,数据独有,用到了写时拷贝技术。
返回值:子进程中返回0,父进程返回子进程的pid,出错则返回-1
此外**vfork()**函数也可以创建进程, pid_t vfork(void) ;
创建子进程,并且阻塞父进程;父子进程共用同一块地址空间;
不熟悉进程创建的可以参考这里

2.进程退出

进程退出共有三种方式:
1.main()函数中使用return;
2.使用库函数exit();
3.系统调用:_exit();
注意1和2两种退出方式会再退出前刷新缓冲区,而_exit()会直接释放掉资源;
可通过perrror(const char *),头文件<stdio.h>,使用时输入空字符串即可; 还有函数strerror(errno),头文件<string.h>,打印出程序错误的原因;其中errno为错误编号。

运行这一小段代码可以查看系统中的错误码0-255都代码表了什么含义,还使用到**perror()**函数打印错误原因;

  #include <stdio.h>    
  #include <string.h>    
  #include <unistd.h>    
  int main ()    
  {    
    int i;    
    for (i=0 ;i<266;i++){    
      printf("%s\n",strerror(i));    
    }    
    pid_t pid = fork();    
    if(pid <0){    
      perror("fork error");    
      return -1;    
    }    
    char *p =NULL;    
    memcpy(p,"aabbcc",3);    
	perror();    
    return 0 ;    
  }        

结果分析

3.进程等待

父进程等待子进程退出,获取子进程的推出返回值,释放子进程的资源,避免其成为僵尸进程。
使用到的函数:
1.pid_t wait(int * status );能够处理任意一个子进程退出;
这个函数接口是一个阻塞函数,如果子进程没有退出会一直等待;
status 为获取到子进程退出的返回值,成功则返回PID,失败 -1(没有子进程);
2.pid_t waitppid(pid_t pid, int*status,int options);能够等待任意一个子进程退出,或等待指定子进程(pid)的退出。
参数: pid 为-1的时候 接受任意一个子进程退出,大于0的时候为指定子进程的pid时则等待指定子进程退出;
status 用于获取子进程退出的返回值
options 0默认为阻塞状态,若将其设置为WNOHANG则为非阻塞状态,这样就比较友好一点,父进程可以边等待子进程的退出,一边执行自己的任务。

如果子进程已退出,则不需要等待。
阻塞与非阻塞
阻塞:为了完成一个任务,发起调用,若当前不具备完成条件,则一直等待;
非阻塞:为了完成一个任务,发起调用,若当前不具备完成条件,则报错返回。

#include <stdio.h>                                                                        
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{    
    pid_t pid = fork();    
    if (pid < 0) {
        perror("fork error");//程序的一种失败错误处理
        return -1;
    }else if (pid == 0) {
        sleep(5);
        exit(99);//退出子进程
    }
    int ret, status;
   //ret = wait(&status);   //使用wait()等待子进程退出,阻塞;
   // if (ret > 0) {
   //    if (WIFEXITED(status)) {
   //         printf("%d\n", WEXITSTATUS(status));
   //     } else {
   //         printf("进程异常退出\n");
   //    }
   // }
    while((ret = waitpid(-1, &status, WNOHANG)) == 0) {   //使用waitpid()等待子进程退出,非阻塞;
        printf("没有子进程退出,打一把麻将\n");
        sleep(1);
    }
    while(1) {
        printf("------------\n");
        sleep(1);
    }
    return 0;
} 

在这里插入图片描述
关于上面status返回值还需要做进一步说明,status保存在一个四字节的空间里,如图:
倒数第最后一个字节后七位保存了异常信号值,异常信号值若为0,则表示进程正常退出,若不为0则表示进程异常退出,在进程异常退出的情况下,返回值是不具有意义的。可使用status & 0x7f 获异常信号也可使用函数**WIFEXITED(status)判断是否异常返回。
第三个字节保存了函数的推出返回值,(status>>8)0xff 可获取其推出返回值,或使用函数
WEXITSTATUS(status)**获取。
在这里插入图片描述

4.进程替换

替换一个正在调度的程序. 子进程通过调用 exec 系列函数时, 加载一个新的程序到内存上,更新当前进程的页表隐映射信息,让当前进程映射到新的页表信息上去。本质上并没有创建新的进程, 进程的 ID 也没有发生变化。
在这里插入图片描述

进程替换会使用到exec 系列函数:

#include<unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

第一个参数:新的程序文件路径名;
第二个参数:程序的运行参数 -l -o 等;
第三个参数:程序的环境变量;

关于这几个函数的区别和简单使用:
1.有没有p的区别:在于程序文件是否需要带路径;有P时可以不带路径,但是程序必须要在PATH环境变量指定的路径下,这样才能找到要运行的程序;

举几个例子:
1.int execl(const char *path, const char *arg, …);与int execlp(const char *path, const char *arg, …, char *const envp[]);

  #include <stdio.h>    
  #include <unistd.h>    
  #include <stdlib.h>    
  int main (int argc, char *argv[])    
  {    
      printf("XXXXX-----XXXXX\n");    
      execl("/usr/bin/ls","ls","-a","-l",NULL);///usr/bin/ls为ls程序路径,可使用whereis ls 命令查看其路径  
      //execlp("ls","ls","-a","-l",NULL)//可以不用给程序的路径但程序要在PATH环境变量指定的路径下 ,运行结果与上面语句运行结果相同。
      printf("DDDDD----DDDDD\n");     
      return 0; 
  }

在这里插入图片描述
2.有没有e的区别:在于程序是否自己设定环境变量,有E就是自己设定(覆盖式),没有e则默认使用自己的。
举个例子:

 #include <stdio.h>    
  #include <unistd.h>    
  #include <stdlib.h>    
  int main (int argc, char *argv[])    
  {    
      printf("XXXXX-----XXXXX\n"); 
     char *env[] = {"MYVAL=1000", NULL}; //这里设置了一个环境变量,注意为覆盖式赋值
     //execlp("./arg", "arg", "-a", "-l", NULL);
     execle("./arg", "arg", "-a", "-l", NULL, env);   //程序替换为arg程序
	printf("DDDDD----DDDDD\n");     
      return 0; 
  }

arg.c代码,可查看函数的命令参数和环境变量

  #include <stdio.h>                                                                      
  #include <unistd.h>    
  #include <stdlib.h>    
      
  //argc 程序运行参数的个数    
  //argv 所有的运行参数的字符串的地址的保存位置    
int main (int argc, char *argv[], char *env[])    
  {    
      int i;    
      for (i = 0; argv[i] != NULL; i++) {    
          printf("argv[%d]=[%s]\n", i, argv[i]);    
      }    
      for (i = 0; env[i] != NULL; i++) {    
          printf("env[%d]=[%s]\n", i, env[i]);    
      }    
      return 0;    
  } 

结果

3.l和v的区别:在于程序运行参数的赋予方式不同,v则表示参数要用列表形式传递,注意第0个参数为程序自身,最后一个参数应为NULL。

 #include <stdio.h>    
 #include <unistd.h>    
 #include <stdlib.h>    
 int main (int argc, char *argv[])    
 {    
    printf("XXXXX-----XXXXX\n"); 
     char *env[] = {"MYVAL=1000", NULL}; //这里设置了一个环境变量,注意为覆盖式赋值
  	char *arg[] = {"./arg", "-a", "-l", NULL};
 	execve("./arg", arg, env); //使用数组来传递运行参数
	printf("DDDDD----DDDDD\n");     
    return 0; 
 }

在这里插入图片描述

4.如图:最后一个函数为系统调用接口,其它五个函数均对其功能做了封装,适应某种特定场景的使用,为库函数。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值