前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >温故Linux后端编程(二):进程

温故Linux后端编程(二):进程

作者头像
看、未来
发布2021-09-18 10:09:51
7030
发布2021-09-18 10:09:51
举报
文章被收录于专栏:CSDN搜“看,未来”

文章目录

什么是进程

一段程序的执行过程。

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

一堆官方话,就·一段程序的执行过程。

关于 操作系统 领域进程的概念:操作系统(2)进程


进程原语

fork

代码语言:javascript
复制
#include <unistd.h>

pid_t fork(void);

功能:子进程复制父进程中的0~3g空间和PCB,但ID号不同。

fork调用一次返回两次

父进程中返回子进程id (就是大于0的意思)

子进程返回0

读时共享写时复制,可保高效

与之相关函数:

代码语言:javascript
复制
#include<sys/types.h>
#include<unistd.h>

pid_t getpid(void); //获取进程ID
pid_t getppid(void); //获取父进程ID
进程的产生方式:

进程的产生有多种方式,但是追本溯源是相通的。

代码语言:javascript
复制
(1)复制父进程的系统环境(放心,只要是你开的进程,肯定有父进程)
(2)在内核中建立进程结构
(3)将结构插入到进程列表,便于维护
(4)分配资源给该进程
(5)复制父进程的内存映射消息
(6)管理文件描述符和链接点
(7)通知父进程

下面是一张进程列表的图,命令:pstree。

可以看到init是所有进程的父进程,其他进程都是由init进程直接或间接fork出来的。

exec族

fork子进程是为了执行新程序(fork创建了子进程后,子进程和父进程同时被OS调度执行,因此子进程可以单独的执行一个程序,这个程序宏观上将会和父进程程序同时进行)

使用exec族函数运行新的可执行程序。exec族函数可以直接把一个编译好的可执行程序直接加载运行。

有了exec族函数后,典型的父子进程程序是这样的:子进程需要运行的程序被单独编写、单独编译链接成一个可执行程序(hello)。主进程为父进程,fork创建了子进程后在子进程中exec来执行hello,达到父子进程分别做不同程序同时(宏观上)运行的效果。

代码语言:javascript
复制
#include<unistd.h>

int execve(const char *path,char *const argv[],char *const envp[]);//这个是真正的系统调用
//以下的函数最后都是调用这个函数

int execl(const char *path,char *const argv,···);
int execlp(const char *file,char *const argv,···);
int execle(const char *path,char *const argv,···· ,char *const envp[]);
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv,);

exec函数族装入并运行程序path/file,并将参数arg0(arg1, arg2, argv[], envp[])传递给子程序,出错返回-1.

看一下后缀:

后缀

功能

l

希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志

v

希望接收到一个以NULL结尾的字符串数组的指针

p

是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件

e

函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境

下面我找到一些通俗易懂的栗子,算是让我明白了一点:

代码语言:javascript
复制
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
  //以NULL结尾的字符串数组的指针,适合包含v的exec函数参数
  char *arg[] = {"ls", "-a", NULL};
  
  /**
   * 创建子进程并调用函数execl
   * execl 中希望接收以逗号分隔的参数列表,并以NULL指针为结束标志
   */
  if( fork() == 0 )
  {
    // in clild 
    printf( "1------------execl------------\n" );
    if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )
    {
      perror( "execl error " );
      exit(1);
    }
  }
  
  /**
   *创建子进程并调用函数execv
   *execv中希望接收一个以NULL结尾的字符串数组的指针
   */
  if( fork() == 0 )
  {
    // in child 
    printf("2------------execv------------\n");
    if( execv( "/bin/ls",arg) < 0)
    {
      perror("execv error ");
      exit(1);
    }
  }
  
  /**
   *创建子进程并调用 execlp
   *execlp中
   *l希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
   *p是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
   */
  if( fork() == 0 )
  {
    // in clhild 
    printf("3------------execlp------------\n");
    if( execlp( "ls", "ls", "-a", NULL ) < 0 )
    {
      perror( "execlp error " );
      exit(1);
    }
  }
  
  /**
   *创建子里程并调用execvp
   *v 望接收到一个以NULL结尾的字符串数组的指针
   *p 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
   */
  if( fork() == 0 )
  {
    printf("4------------execvp------------\n");
    if( execvp( "ls", arg ) < 0 )
    {
      perror( "execvp error " );
      exit( 1 );
    }
  }
  
  /**
   *创建子进程并调用execle
   *l 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
   *e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
   */
  if( fork() == 0 )
  {
    printf("5------------execle------------\n");
    if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )
    {
      perror("execle error ");
      exit(1);
    }
  }
  
  /**
   *创建子进程并调用execve
   * v 希望接收到一个以NULL结尾的字符串数组的指针
   * e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
   */
  if( fork() == 0 )
  {
    printf("6------------execve-----------\n");
    if( execve( "/bin/ls", arg, NULL ) == 0)
    {
      perror("execve error ");
      exit(1);
    }
  }
  return EXIT_SUCCESS;
}

wait/waitpid

这里几个概念:

僵尸进程:子进程退出,父进程没有及时回收,子进程成为僵尸进程

孤儿进程:父进程退出,而子进程没有退出,子进程成为孤儿进程

init进程:1号进程,负责收留孤儿进程,成为他们的父进程

代码语言:javascript
复制
有几种方式终止进程:
(1)main返回
(2)调用exit
(3)调用_exit
(4)调用abort(给自己发送异常终止信号)
(5)由一个信号终止
代码语言:javascript
复制
#include<sys/types.h>
#include<sys/wait.h>

pid_t wait(int *status);
//这里的status为一个整形指针,是该子进程的返回状态。若该指针不为空,则可以通过该指针获取子进程退出时的状态。

pid_t waitpid(pid_t pid,int *status,int options);
// pid是进程号
/*
	<-1 回收指定进程组内的任意子进程
	-1 回收任意子进程
	0 回收和当前waitpid调用一个组的所有子进程
	>0 回收指定ID的子进程
*/
//options:
/*
WNOHANG:强制回收,不阻塞。
WUNTRANCED:一般用上面那个
*/

最后的惊喜

它还能跨语言!!!!!


就到这里啦,点赞评论收藏,一键三连哦(如果你真的看到这里的话)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 什么是进程
  • 进程原语
    • fork
      • 进程的产生方式:
    • exec族
      • wait/waitpid
        • 最后的惊喜
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档