Linux——进程控制:创建、终止、等待、替换

news/2024/11/16 22:32:40/

进程创建

fork

#include <unistd.h>
pid_t fork(void);

操作系统做了什么?

调用fork之后,内核的工作:

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构内容拷贝至子进程
  3. 添加子进程到系统进程列表当中
  4. fork返回,开始调度器调度

进程 = 内核数据结构 + 代码和数据

创建子进程的过程就是:创建子进程的内核数据结构,即task_struct + mm_struct + paper_table等等,继承父进程的代码,同时拷贝数据。

fork之后,操作系统会create一个新的process,copy_process,copy_mem,把父进程的所有资源(包括代码段,数据段、堆栈数据等),以及寄存器的值继承给子进程。之后再以写时拷贝的形式保证进程的数据独立性。

do_fork

int sys_fork(struct pt_regs *regs)
{return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
}asmlinkage int sparc_do_fork(unsigned long clone_flags,unsigned long stack_start,struct pt_regs *regs,unsigned long stack_size)
{unsigned long parent_tid_ptr, child_tid_ptr;unsigned long orig_i1 = regs->u_regs[UREG_I1];long ret;parent_tid_ptr = regs->u_regs[UREG_I2];child_tid_ptr = regs->u_regs[UREG_I4];ret = do_fork(clone_flags, stack_start,regs, stack_size,(int __user *) parent_tid_ptr,(int __user *) child_tid_ptr);/* If we get an error and potentially restart the system* call, we're screwed because copy_thread() clobbered* the parent's %o1.  So detect that case and restore it* here.*/if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)regs->u_regs[UREG_I1] = orig_i1;return ret;
}

fork返回之后,两个进程的PC指针都指向fork函数之后的代码,但实际上是父子进程共享了整个代码。子进程运行的时候是从pc指针中取值得到下一次运行的代码的地址。
当然,子进程完全可以通过goto等方法让执行流回到fork之前的地方。

一个有趣的现象

#include <stdio.h>
#include <unistd.h>
#include <cstdlib>int main()
{printf("我是父进程,我要来执行fork了\n");pid_t id = fork();again:  printf("这是fork之后的代码,子进程跳转后才能访问到的\n");if(id == 0){printf("我是子进程 pid = %d, ppid = %d\n",getpid(), getppid());for(int i = 0; i < 3; i++)sleep(1);printf("子进程要跳转了\n");goto again;exit(1);}while(1){printf("我是父进程 pid = %d, ppid = %d\n",getpid(), getppid());for(int i = 0; i < 3; i++){sleep(1);}break;}return 0;
}


Ctrl+C杀掉进程后,命令行开始提供服务,过了一会还会继续打印?这里看起来是Ctrl+C不起作用
这不难理解,./a.out之后,运行的是从始至终的父进程,父进程会被杀掉,但是子进程还在一直运行。父进程挂掉之后,shell继续提供服务,但是子进程还在向显示器打印。要用kill命令发送信号杀掉子进程,子进程会进入终止态,但是因为父进程早就挂掉了,所以子进程会被init领养,之后被回收资源。


进程终止

进程退出

进程退出的场景:运行完毕,结果正确;运行完毕,结果不正确;运行没完毕,被终止。
echo $?命令可以查询最近一次进程执行完毕时对应进程的退出码,退出码是一个8位无符号数。

main函数为什么要return,return给谁,为什么是0?
为了向操作系统或者其他程序报告自己的运行状态。
函数的返回值为0表示成功,因为成功只有一个标志就行,不需要知道原因,失败可以有无数个原因。
例如STM32开发中调用MPU6050的DMP库的初始化函数,如果不成功会有多种错误原因,用正整数可以标识。

如何终止进程?

#include <unistd.h>
void _exit(int status);
void exit(int status);

正常终止

  1. 在main函数中return
  2. 用exit或者_exit函数
    区别:exit会终止进程且刷新缓冲区,但是_exit不会。
    调用exit,系统会关闭所有打开的流,刷新缓冲区,之后再调用_exit。

虽然status是int,但是操作系统会把int转换成uint,因为退出码的范围是0-255。所以exit(-1)时,echo $? 结果是255。

异常终止

如收到信号。


进程等待

为什么要等待?

如果子进程退出,父进程不回收就会变成僵尸进程,操作系统是无法杀死的,可能内存泄漏,除非父进程也结束了,子进程被操作系统领养。因此父进程应该等待子进程,回收子进程的资源,获取其退出信息,例如退出码。

如何进程等待?

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);
pid_t waitpid(pid_t pid, int* status, int options);

调用这两个函数,可以从task_struct中拿到进程的退出状态,退出状态是进程退出后仍留在数据结构中的。

wait

  • wait的参数是输出型参数,等待任意一个子进程,并输出退出信息。

waitpid

  • pid:-1表示任意进程,大于0的数表示要等待的进程的pid。
  • status:该进程的退出状态。
  • options:0表示阻塞等待、WNOHANG表示非阻塞等待。
  • 返回值:大于0表示等待成功,并且进程已退出;等于0表示等待成功,但是进程还没有退出。

进程阻塞

调用scanf和cin的时候,没有输入就会一直等待,task_struct的R状态变为S状态,从运行队列转移到等待队列。

非阻塞等待

等待的时候如果事件没就绪就直接返回,执行别的东西,待会再来检查。多次调用费阻塞等待接口的检测行为称为轮询检测

等待的结果

退出码的组成

status的低八位是进程的退出信号,高8位是进程的退出码。当进程被杀死了还会有一个core_dump标志位。

printf("退出信号 = %d, 退出码 = %d\n", (status & 0x7F), (status >> 8) & 0x7F);

例如:当用kill杀死进程的时候,比如kill -9 进程, 进程的低八位就会收到9号信号。
如果一个进程收到信号,就说明进程出现异常,这时候退出码就没用了,因为不是正常退出,所以只关心收到的信号。


进程替换

什么是进程替换?

之前创建进程后,父子进程共享代码。如果想让子进程执行其他逻辑呢?

  1. 进程替换不会创建新进程,因为进程替换只是将该进程的数据替换为指定的可执行程序。而进程PCB没有改变,所以不是新的进程,pid不变。
  2. 进程替换后,如果替换成功则开始执行新程序,原替换函数之后的代码不会执行,因为进程替换是覆盖式的替换,替换成功后进程原来的代码就消失了。如果进程替换失败则会执行原替换函数后的代码。

原理

将磁盘中的别的程序加载到内存中,通过写时拷贝,重新建立页表的映射,将原子进程中数据段和代码段的映射改变为到别的程序的映射,这样父子进程的代码和数据就完全分离了。

进程替换的接口

#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[]);

execl

  • path:用来替换的程序路径:/usr/bin/ls,也可以是相对路径。要执行一个程序,系统需要知道如何执行这个程序,就要带参数:ls -a -l
  • arg:表示可变参数,用于传入选项。以NULL为终止符表示参数传递完毕。
  • 返回值不用判断,因为替换成功不会返回,替换失败将会执行原文件后续代码。
  • 调用方式:execl ("/usr/bin/ls", "ls", "-a", "-l", NULL);

execlp

(p代表环境变量path)

  • file:要替换的程序名,可以是相对路径,也可以在环境变量中搜索。
  • arg:可变参数,跟上面一样。
  • 返回值:不用判断
  • 调用方式:execlp(“./hello”,“./hello” ,NULL);

execv和execvp

跟execl的区别是可变参数变成字符串数组。

char* commands[] = {"./hello", NULL};                                        
std::cout << "我要替换了\n";
execv("./hello",commands);

execle和execve

则是可以自己导入环境变量。


http://www.ppmy.cn/news/560919.html

相关文章

服务器系统怎么打驱动精灵,win7系统如何使用驱动精灵?教你在win7系统使用驱动精灵的方法...

驱动精灵是一款驱动管理软件&#xff0c;功能性非常强&#xff0c;可以帮助用户安装驱动、备份驱动、卸载驱动等等。好多小伙伴在win7系统上安装驱动精灵却不知道如何使用&#xff1f;网上也有很多相关教程&#xff0c;但不够详细&#xff0c;因此&#xff0c;这里系统城小编来…

微波炉控制器的设计(EDA课程设计)

微波炉控制器的设计&#xff08;Quartus 9.1&#xff09; 1.设计一个具有定时和信息显示功能的微波炉控制器。 2.要求改微波炉控制器能够在任意时刻取消当前工作&#xff0c;复位为初始状态。 3.可以根据需要设置烹调时间的长短&#xff0c;系统最长的烹调时间为59分59秒&…

蠕动泵的常见参数及常用电机驱动芯片---Trinamic(TMC)

蠕动泵是一种可控制流速的液体输送装置&#xff0c;因为可以稳定控制流体的流速&#xff1b;无污染输送流体&#xff1b;维护简单便宜&#xff0c; 且具有输送精度高、较强的耐腐蚀性、剪切作用小、操作简单及易于维护等优势&#xff0c; 所以广泛应用在科研、制药、化工、环…

服务器系统安装蓝牙驱动,win10蓝牙驱动怎么安装?-win10蓝牙驱动的安装教程 - 河东软件园...

蓝牙是现在设备连接中使用的比较广泛的硬件之一&#xff0c;除了需要硬件支持以外&#xff0c;我们的电脑上也需要安装蓝牙驱动&#xff01;在我们将电脑升级到Win10之后&#xff0c;系统中是自带了蓝牙驱动的&#xff0c;不过据很多使用正式版系统的用户反映&#xff0c;电脑中…

TMC6300-LA-T-单轴BLDC驱动芯片-超低待机功耗

TMC6300 产品概述: 高效低电压、零待机电流驱动器&#xff0c;适用于峰值高达 2A 的 3 相 BLDC/PMSM 电机&#xff0c;三重半桥&#xff0c;带独立的 HS 和 LS 控制信号。 *自带保护和诊断功能以保证稳健和可靠的运行。 *集成电荷泵提供超低的RDSon和超低待机电流&#xff0…

53款热门驱动电机详细参数,拿走不谢

提供《新能源驱动电机对标样件》电驱动系统、电驱动总成样件清单、驱动电机目录、新能源电机、三合一电驱动、永磁同步电机、异步电驱动、油冷扁线电机、电机控制器、逆变器、差速器、电驱动桥、电机控制器的作用 切磋&#xff1a;shbinzer 切磋&#xff1a;shbinzer 切磋&…

使用tb6612模块驱动直流电机

tb6612所有引脚如图所示 tb6612模块使用方法为 &#xff1a; STBY高电平&#xff0c;提供pwm脉冲给PWMA&#xff0c;PWMB&#xff0c;设置AIN1&#xff0c;AIN2&#xff0c;BIN1&#xff0c;BIN2控制电机正反转。 引脚讲解 VM :5V-10V电压 VCC:逻辑电平输入&#xff08;接到…

唯样商城:英飞凌 —— 一文弄懂IGBT驱动

我们都知道&#xff0c;电机驱动是IGBT的主要应用领域之一。有的同学可能会有这样的困惑&#xff1a; “IGBT本来就是驱动电机的&#xff0c;为什么它自己还需要一个驱动&#xff1f;IGBT驱动到底是做什么的&#xff1f;” 这个问题说来简单&#xff0c;就像《极简电力电子学》…