linux之进程控制

目录

一、fork函数

1.进程:

2.fork函数:

3.写时拷贝

4.fork常规用法

5.fork调用失败的原因

二、进程终止

1.终止是在做这么?

2.进程终止的3种情况

3.如何终止

三、进程等待

四、进程程序替换

1.替换原理

2.原理

3.将代码改成多线程

4.认识这些函数

1.execl

2.execv

3.execlp/execvp 

4.execle/execvpe


一、fork函数

1.进程:

        内核的相关管理数据结构(task_struct + mm_struct + 页表) + 代码和数据

2.fork函数:

        在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

进程调用fork,当控制转移到内核中的fork代码后,内核做:

        分配新的内存块和内核数据结构给子进程

        将父进程部分数据结构内容拷贝至子进程

        添加子进程到系统进程列表当中

        fork返回,开始调度器调度

fork函数返回值:子进程返回0,父进程返回的是子进程的pid。

为什么fork返回值是这样? 为了让父进程对子进程进行标识,知道子进程的pid,可以管理子进程。

3.写时拷贝

1.概念:

        通常,父子代码共享,父子不再写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。

虚拟地址一样,物理地址却不一样。

4.fork常规用法

        一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

        一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

5.fork调用失败的原因

        系统中有太多的进程

        实际用户的进程数超过了限制

二、进程终止

1.终止是在做这么?

        释放曾经的代码和数据所占据的空间

        释放内核数据结构

2.进程终止的3种情况

#include <stdio.h>    
#include <unistd.h>    int main()    
{    printf("i am a process! pid:%d, ppid:%d\n",getpid(), getppid());    sleep(3);    return 100;    
}    
~

echo:内建命令,打印的都是bash内部的变量数据

? : 父进程bash获取到的,最近一个子进程退出的退出码

退出码 : 告诉关心方(父进程),我把任务完成的怎么样了

0:成功    !0 : 失败

1,2,3,4,5....不同的非0值,一方面表示失败,一方面表示失败的原因

for(int errnum = 0; errnum < 240; ++errnum)printf("%d -> %s\n",errnum, strerror(errnum));  //可以打印出每个数字代表的信息

为用户负责,可以了解错误信息

#include <stdio.h>                                                                                                                                   
#include <unistd.h>
#include <string.h>enum
{success=0,Div_zero,Mod_zero,
};    const char* codeToerrstring(int code)    
{    switch(code)       {    case success:     return "success";    break;        case Div_zero:          return "Div_zero";    break;        case Mod_zero:     return "Mod_zero";    break;    default:    return "unknow error";    break;    }    
}    int exit_code = 0;int Div(int x, int y)
{if(x == 0){exit_code = Div_zero;return -1;}else{exit_code = success;return x / y;}
}int main()
{int result = Div(0,2);printf("result = %d , %s\n",result, codeToerrstring(exit_code));return exit_code;    
}

 代码跑完结果正不正确,可以通过进程的退出码来决定!

出错原因:系统 或者 自定义退出码。

代码执行时,出现异常,提前退出。

进程出异常,本质是进程收到OS发给进程的信号,提前终止

        一旦出现异常,退出码便没有意义。

1.先确定是否异常

2.不是异常,就一定是代码跑完了,看退出码就行

衡量一个进程退出,我们只需两个数字:退出码,退出信号!

退出码 退出信号释义
00运行成功
!00运行结果不对
!0!0进程异常
0!0

3.如何终止

1> main函数return,表示进程终止(非main函数 - return,函数结束)。

2> exit() -- C库函数。 代码调用exit函数。注:我们代码的任意位置调用exit,都表示进程终止。

3> _exit()  -- system call(系统调用) , exit会在进程退出时,冲刷缓冲区,而_exit不会。其它不变

注:exit底层调用_exit,目前我们所说的缓冲区,不是内核缓冲区。

三、进程等待

        结论:任何子进程,在退出的时候,一般必须要被父进程进行等待。进程在退出的时候,如果父进程不管不顾,退出进程,子进程就会进行状态Z(僵尸进程),导致内存泄漏

       why?

        1.父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)

        2.获取子进程的退出信息,知道子进程是什么原因退出的。(可选的功能)

        

        怎么办?

        wait/waitpid

         pid_t wait(int *status),等待父进程时,任意一个子进程退出,即等待成功。 等待成功时,返回子进程的pid。

        如果子进程没有退出,父进程其实一直在进行阻塞等待!

        pid_t waitpid(pid_t pid, int *status, int option)

       返回值:当正常返回的时候waitpid返回手机的子进程的进程id

                      如果设置了选项WNOHANG,而调用中waitpid发现没有已退出

           的子进程可收集,则返回0 

                      如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。   

参数:pid: Pid = 1,等待人一个子进程。与wait等效

                    Pid = 0,等待其进程ID与pid相等的子进程。 

        int status (32位)

        退出信息:1.输出型参数 

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>                                                                                                                                
#include <stdlib.h>void ChildRun()    
{    int cnt = 5;    while(cnt--)    {    printf("i am child, cnt:%d pid:%d, ppid:%d\n", cnt, getpid(), getppid());    sleep(1);    }    
}    int main()    
{    printf("i am father, pid:%d, ppid:%d\n", getpid(), getppid());    pid_t id = fork();    if(id == -1) return 1;    if(id == 0)    {    //child    ChildRun();    printf("child quit...\n");    exit(1);    //异常退出}    sleep(7);    //parent    //pid_t rid = wait(NULL);    int status = 0;    pid_t rid = waitpid(id, &status, 0);    if(rid > 0)    {    printf("wait successful, rid:%d\n", rid);    }    else    {    printf("wait file!\n");    }    sleep(3);    printf("father quit,status:%d, child quit code:%d, child quit signal:%d\n",status, (status >> 8) & 0xFF, status & 0x7F);                           return 0;    
}

如果子进程是一个死循环,这时我们通过kill -9 3925发出一个信号终止子进程的话,就会得到

 如果我们在子进程中加入一个野指针异常,比如:

void ChildRun()      
{      int cnt = 5;      int* p =NULL;      while(1)      {      printf("i am child, cnt:%d pid:%d, ppid:%d\n", cnt, getpid(), getppid());            sleep(1);      *p =100;   //野指针                                                           }      
} 

 上述status使用了为运算的方式,对于运用不是很友好

        WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

        WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

include <stdio.h>                                                                                                                                       
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>void ChildRun()
{int cnt = 5;//int* p =NULL;while(cnt--){printf("i am child, cnt:%d pid:%d, ppid:%d\n", cnt, getpid(), getppid());sleep(1);//*p =100;}
}int main()
{printf("i am father, pid:%d, ppid:%d\n", getpid(), getppid());pid_t id = fork();if(id == -1) return 1;if(id == 0) {//childChildRun();printf("child quit...\n");exit(123);}//fatherwhile(1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG);//non blockif(rid == 0){printf("child is running, father check next time!\n");usleep(1000);//DoOtherThing();}else if(rid > 0){if(WIFEXITED(status)){printf("child quit success! child quit code:%d\n",WEXITSTATUS(status));} else{printf("child quit unsuccess! child quit code:%d\n",WEXITSTATUS(status));}break;}else{printf("wait filed!\n");}}return 0;
}

WNOHANG:非阻塞式等待

四、进程程序替换

1.替换原理

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


#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 execvpe(const char *path, char *const argv[], char *const envp[]);

1.execl

#include <stdio.h>
#include <unistd.h>int main()
{printf("begin execl...\n");execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("end execl...\n");return 0;
}

2.原理

进程的程序替换

        没有创建新的进程        

 站在被替换进程的角度,本质上就是这个程序被加载的内存中了

exec*系列的函数,执行完毕后,后续代码不见了,因为被替换了

execl函数的返回值不关心,因为替换成功,将不会向后继续运行,只要继续向后运行,则一定是运行失败了

类似“夺舍”。

3.将代码改成多线程

fork创建子进程,让子进程自己去替换 ,父进程wait

        既可以让子进程完成了任务,父进程还不受影响。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("begin execl...\n");pid_t id = fork();if(id == 0){//childsleep(2);execl("/usr/bin/ls", "ls", "-a", "-l", NULL);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0); //blockif(rid > 0){printf("father wait success! child exit code:%d\n", WEXITSTATUS(status));}printf("end execl...\n");return 0;
}

 父子进程具有独立性,当代码数据改变时,则子进程会发生写时拷贝。

4.认识这些函数

1.execl

 l:list 列表。    path:我们要执行的程序,需要带路径。

例:ls -a -l    execl(“/usr/bin/ls”,“ls”,“-a”,“-l”,NULL) ,结尾以NULL结束

2.execv

v:vector

int main()
{printf("begin execl...\n");pid_t id = fork();if(id == 0){//childsleep(2);char *const argv[] = {"ls","-a","-l","--color",NULL,};execv("usr/bin/ls", argv);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0); //blockif(rid > 0){printf("father wait success! child exit code:%d\n", WEXITSTATUS(status));}printf("end execl...\n");return 0;
}

3.execlp/execvp 

 p:用户可以不传要执行程序的路径(但是文件名要传),直接告诉它要执行谁就行。

        查找这个程序,系统会自动在系统环境变量PATH中查找

例:execlp("ls", "ls", "-a", "-l", NULL);          execvp("ls", argv);

4.execle/execvpe

e:environment


回到execl,我创建一个程序,并且要在testexec.c中运行它,并且打印它的pid

它们的pid均为22166,证明没有创建新的进程。


envp:整体替换所有的环境变量

1.用全新的自己写的给子进程

2.用老的环境变量给子进程,enverion

3.老的环境变量稍微修改,给子进程  putenv("");

5.真正的系统调用:execve

其它exec函数都是execve的封装,底层都是调用的这个函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/579184.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 学习第五弹——语句

一、if语句 —简单if语句 —if else 语句 —if else if else 语句 1、简单if语句 if&#xff08;表达式&#xff09;{语句} &#xff08;1&#xff09;表达式必须使用圆括号括起来&#xff1b; &#xff08;2&#xff09;表达式&#xff1a;关系表达式或逻辑表达…

蓝桥杯第十五届抱佛脚(五)DFS、BFS及IDS

蓝桥杯第十五届抱佛脚&#xff08;五&#xff09;DFS、BFS及IDS 深度优先搜索 DFS(Depth-First Search)即深度优先搜索,是一种用于遍历或搜索树或图的算法。它从根节点开始,尽可能沿着每一条路径直到这条路径最后一个节点被访问了,然后回退,继续访问下一条路径。它的基本思想…

wiztree免费的c盘清理软件

现如今&#xff0c;我们无论是工作还是学习&#xff0c;都已经离不开电脑了&#xff0c;经常使用电脑就会导致电脑的“垃圾”越来越多&#xff0c;从而导致磁盘爆红。 相信大家多多少少都体会过这大红的压迫感吧&#xff0c;想清理但是不敢删&#xff0c;不清理吧软件用不了&a…

XVQQ防撤回多开1.7

资源获取 链接&#xff1a;https://pan.baidu.com/s/1B7gqb8pQbyRA1xmUH7h-6w?pwd00qt 提取码&#xff1a;00qt

linux离线安装maven

一、下载maven 地址&#xff1a;Maven – Download Apache Maven 使用root权限用户登录服务器 cd /opt sudo mkdir maven cd maven 二、上传maven 使用Xftp工具 三、解压并配置环境变量 tar -zxvf tar -zxvf apache-maven-3.9.6-bin.tar.gz cd apache-maven-3.9.6/ 看到解压…

linux在使用重定向写入文件时(使用标准C库函数时)使处理信号异常(延时)--问题分析

linux在使用重定向写入文件时(使用标准C库函数时)使处理信号异常(延时)–问题分析 在使用alarm函数进行序号处理测试的时候发现如果把输出重定向到文件里面会导致信号的处理出现严重的延迟(ubuntu18) #include <stdio.h> #include <stdlib.h> #include <unist…

FastAPI+React全栈开发08 安装MongoDB

Chapter02 Setting Up the Document Store with MongoDB 08 Installing MongoDB and friends FastAPIReact全栈开发08 安装MongoDB The MongoDB ecosystem is composed of different pieces of software, and I remember that when I was starting to play with it, there w…

代码随想录训练营第60天 | LeetCode 84.柱状图中最大的矩形、总结

LeetCode 84.柱状图中最大的矩形 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;单调栈&#xff0c;又一次经典来袭&#xff01; LeetCode&#xff1a;84.柱状图中最大的矩形_哔哩哔哩_bilibili 思路 代码如下&#xff1a; ​​​​​​总结 感…

1.1 单片机的概念

一,单片机的概念 单片机(Single-Chip Microcomputer),也被称为单片微控制器,是一种集成电路芯片。它采用超大规模集成电路技术,将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、…

DFS:二叉树的深搜与回溯

一、计算布尔二叉树的值 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool evaluateTree(TreeNode* root) {if(root->leftnullptr) return root->val0?false:true; bool left evaluateTree(root->left);bool rightevaluateTree(root->rig…

FFMPEG C++封装(一)(C++ FFMPEG)

1 概述 FFMPEG是一个C语言开源视音频编解码库。本文将FFMPG4.1.3进行C封装&#xff0c;形成C FFMPG库。 2 架构 架构图如下所示&#xff1a; 架构说明: Init 初始化FFMPEG库。IStream 输入流&#xff0c;FFMPEG的输入音视频文件。Packet 音视频数据包Decoder 音视频编码器F…

备忘录模式实战运用(对象快照与恢复)

目录 前言 UML plantuml 类图 实战代码 模板 Originator Memento CareTaker Client 前言 使用备忘录模式&#xff0c;主要目的就是创建对象副本&#xff0c;保存对象内部状态的的快照&#xff0c;以便在需要时恢复对象至某一个历史状态。 仅仅从创建副本来看&#…