Linux学习笔记--进程控制

Posted 水澹澹兮生烟.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习笔记--进程控制相关的知识,希望对你有一定的参考价值。

目录

一.进程创建

1.1写实拷贝

二.进程终止

2.1进程终止退出场景

2.2常见的终止方法

2.3exit()与_eixt()函数的区别

2.3.1回调函数

2.3.2刷新缓冲区

三.进程等待

3.1作用

3.2进程等待的方法

3.2.1wiat()

3.2.2waitpid()

四.进程程序替换

4.1替换原理

4.2替换函数(exec函数簇)

4.2.1常用函数

4.2.2总结exec()

4.3exec常用场景

4.3.1bash应用场景

4.3.2守护进程


一.进程创建

1.1写实拷贝

一般情况下,父子进程代码共享,数据独有,如下图:


二.进程终止

2.1进程终止退出场景

  1. 代码执行完成且正确
  2. 代码执行完成,但是不正确
  3. 代码异常终止

2.2常见的终止方法

  1. main函数中return
  2. exit()函数,_eixt()函数
  3. cttrl+c

  介绍exit()和_eixt()函数:

void exit(int status);
  • 他是一个库函数
  • 谁调用谁退出
  • status是进程退出的状态码
void _eixt(int status);
  • 他是一个系统调用函数
  • 谁调用谁退出

eixt()的内部封装了_eixt(),eixt()在退出时要比_eixt()多执行两个步骤,分别是:用户自定义的清理函数和刷新缓冲区。

2.3exit()与_eixt()函数的区别

2.3.1回调函数

int atexit(void (*function)(void));
//先注册回调函数
//再调用回调函数

参数为函数指针类型,接受一个函数的地址,函数的返回值为void类型,参数也是void类型。atexit()是注册了一个函数mycallback,注册并非是调用。当main函数结束后,才会调用刚刚注册的mycallback()。

代码展示:

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

void mycallback(void){
    printf("i am callback!!\\n");
}


int mian(){
    atexit(mycallback);
    printf("hello world\\n");
    return 0;
}

2.3.2刷新缓冲区

刷新缓冲区的几种办法:

  1. main()中的return返回后刷新
  2. fflush();
  3. \\n 进行刷新
  4. eixt()

三.进程等待

3.1作用

父进程调用等待的方法,等待子进程退出,防止子进程变成僵尸进程。

3.2进程等待的方法

3.2.1wiat()

pid_t wait(int *status);

参数:输出型参数,将wait函数内部计算的结果通过status返回给调用者;

在这里给出其他的一些参数:输入型参数:调用者给被调用者传参;输出型参数给指针,输入型参数给引用。

status扩展:如何判定当前程序一定是正常或者异常退出呢?

在这里我们要知道,并不是所有异常都会产生coredump标志位,当发生硬限制(磁盘因素,内存空间不足)或者软限制时不会产生,因此直接用coredump标志位直接判断是不可取的。因此我们只能查看是否有退出信号。

举例:现在给你了值status,它调用了wiat()函数,现在wait将status将其后两个字节进行填充,那么判断如下:status&0x7F。

判断是否有coredump标志位:(status>>>7)&0x1

判断退出码:s(status>>8)&0xFF

代码实展示:

#include<stdio.h>
#include<unistd.h>
#include<wait.h>
int main(){
     pid_t pid=fork();
     if(pid < 0){
         printf("create process failed");
         return 0;
     }else if(pid == 0){
         printf("i am child process!!!\\n");
         sleep(5);
     }else{
         while(1){
             wait(NULL);
             printf("i am father process\\n!");
             sleep(2);                                                                                                           
         }
     }
 }

阻塞的概念:在调用结果返回之前,当前线程(执行流)会被挂起,并在得到结果之后返回。

3.2.2waitpid()

pid_t wiatpid(pid_t pid,int *status,int options);

 

参数
pid=-1等待人一个子进程,与wiat等效
pid>0等待其进程ID与pid相等的子进程
status同wait
options为0阻塞模式
options为WNOHANG非阻塞模式

waitpid()中非阻塞模式一定要与循环一起使用。非阻塞模式时,如果不能立刻得到结果,则该调用者不会阻塞当前线程,因此非阻塞模式的情况,调用者需要定时轮询查看状态。waitpid默认阻塞等待任意或者指定子程序退出,当options被设置为WNOHANG,那么此时此时无子程序退出,waitpid返回值为0.


四.进程程序替换

替换正在运行的程序,让正在运行的进程执行其他程序。

4.1替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

4.2替换函数(exec函数簇)

4.2.1常用函数

a.execl()函数

int execl(const char* path,const char* arg,...);

 参数:

path:待要替换的可执行程序,需要指定该程序再那个路径下

arg:可执行程序传递的命令行参数,在这要注意,第一个参数必须是可执行程序本身;可变参数列表(...)也是填充可执行程序的参数们需要以NULL结尾

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1

代码展示:

b.execlp()函数

int execlp(const char* file,const char* arg,...);

 参数:

file:带要替换的可执行程序,可以不给出路径,单数一定再PATH环境变量中可以找得到

arg:可执行程序传递的命令行参数,在这要注意,第一个参数必须是可执行程序本身;可变参数列表(...)也是填充可执行程序的参数们需要以NULL结尾

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1

c.execle()函数

int execle(const char* path,const char* arg,...,char *const envp[]);

 参数:

path:待要替换的可执行程序,需要指定该程序再那个路径下

arg:可执行程序传递的命令行参数,在这要注意,第一个参数必须是可执行程序本身;可变参数列表(...)也是填充可执行程序的参数们需要以NULL结尾

envp[]:表示程序员自己组织的环境变量

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1

d.execv()函数

int execv(const char* path,char *const argv[]);

 参数:

path:待要替换的可执行程序,需要指定该程序再那个路径下

argv[]:它是一个指针数组,保存的是给可执行程序传递的参数(这里要注意,数组的第一个元素为可执行程序本身,最后一个为NULL)

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1

e.execvp()函数

int execvp(const char* file,char *const argv[]);

参数:

file:带要替换的可执行程序,可以不给出路径,单数一定再PATH环境变量中可以找得到

argv[]:它是一个指针数组,保存的是给可执行程序传递的参数(这里要注意,数组的第一个元素为可执行程序本身,最后一个为NULL)

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1 

f.execve()函数

int execve(coanst char* filename,char *const argv[],char *const envp[]);

 参数:

filename:带有路径的可执行程序

argv[]:它是一个指针数组,保存的是给可执行程序传递的参数(这里要注意,数组的第一个元素为可执行程序本身,最后一个为NULL)

envp[]:表示程序员自己组织的环境变量,最后一个参数为NULL

返回值:如果替换成功,就执行其他程序了,如果替换失败,返回-1 

4.2.2总结exec()

a.函数名称当中带p与不带p区别:如果函数名称中带p,则会自动搜索环境变量,而不带p则不会自动搜索。

b.函数名称中带e与不带e的区别:如果函数名称带e,表示程序员需要自己组织环境变量,不带则不需要自己组织。

c.函数名称中带l与不带l的区别:如果函数名称中带l,则表示函数的参数为可变参数列表形式。

d.函数名称中带v与不带v的区别:如果函数名称中带v,表示参数用数组。

e.替换之前的进程与替换之后的进程,他们的进程号是否是一样的? 

4.3exec常用场景

4.3.1bash应用场景

子进程被创建出来后,子进程进行进程程序替换。 

4.3.2守护进程

  1. 启动业务进程时并非直接启动业务进程,而是启动守护进程;
  2. 让守护进程创建一个子进程,让子进程进程程序替换业务程序进程;
  3. 守护进程和业务进程进行进程通信,让守护进程得知业务进程的情况。

业务进程正常,则守护进程不做任何处理,业务进程崩溃或者异常,则守护进程重新拉起来一个子进程,让子进程再进行进程程序替换。

以上是关于Linux学习笔记--进程控制的主要内容,如果未能解决你的问题,请参考以下文章

Linux学习笔记:001Linux内核分析

linux 学习笔记 20160621

Linux内核分析——第六周学习笔记20135308

Linux学习笔记—— Linux进程概念

MOOC《Linux操作系统编程》学习笔记-实验四

学习笔记291—linux命令中ps -ef详解