进程控制a

简介: 进程控制a

创建子进程

如何创建一个进程:当我们执行一个可执行文件时,便会创建一个进程。

如何创建一个子进程:

pid_t fork(void);

返回值:失败返回-1 成功:父进程返回子进程的pid,子进程返回0

注:不是fork能够返回两个值,是在父子进程中返回的值不同。

那如何做到的呢?我们看下fork()创建子进程做了哪些事情。(我们前面是不是说过每一进程都会有自己的一个虚拟地址空间)

循环创建子进程:

先想一个问题:最后有几个进程????

for(int i=0;i<3;i++){
    fork();
}

         

卧槽,8个进程。。。。。。。2^n个进程

我们是不是不让子进程去创建子进程呀,不让子进程去fork()

fork()不是在子进程和父进程中的返回值不一样嘛?那我们不就可以通过 if 来控制嘛!

for(int i=0;i<3;i++){
    pid_t pid = fork();
    if(pid == 0){
        break;
    }
}

 


进程共享(重点  重点  重点)

       说起探讨进程共享,其实就是fork()之后,父子进程之间的相同与不同。

       fork()会把父进程的虚拟地址空间复制一份给子进程(可以理解为深拷贝哦),因为每一个进程都有自己的虚拟地址空间。但是每一个进程的ID都不一样,所以不是简单的复制。

       父子进程相同之处:(用户区基本一样、内核区大部分一样)

               全局变量(.data  .bss)  .text  栈  堆  环境变量  用户ID 宿主目录  进程工作目录  信号处理方式。

       父子进程的不同之处:进程ID  fork()返回值  父进程ID 进程运行时间  闹钟  未决信号集

       总结:似乎,子进程复制了父进程0-3G用户空间内容,以及父进程的PCB,但是pid不同。真的fork()一个子进程都要复制一个父进程的嘛,然后映射到物理内存。。。。

       父子进程之间的全局变量是共享的嘛??

       父子进程之间的全局变量当然不是共享的,因为它们处在不同的虚拟地址空间中。

为了节省系统开销,父子进程之间采用读时共享写时复制的机制。也就是说,只有读操作的时候,父子之间共享同一块物理内存(也就是虚拟地址空间映射的那块),但是一旦有写操作了,就会进行复制物理内存。

       父子之间共享(非常重要,进程间通信的基础):

              文件描述符表         mmap建立的映射区

特别的:父子进程之间谁先执行,谁后执行是根据内核中调度算法决定。


多进程GDB调试(-g 能够将二进制指令对应的源代码对应上,GDB调试的基础)

       使用GDB调试的时候,GDB默认只能跟踪一个进程,可以在fork()函数调用之前,通过指令设置GDB调试工具跟踪父进程或者跟踪子进程。如果不设置,默认跟踪父进程。

       设置调试父进程还是子进程:

                       set  follow-fork-mode   parent/child

       设置调试模式:

                       set detach-on-fork    on/off

                       默认认为on,表示调试当前的时候,其它进程继续执行。

                       off,调试当前进程的时候,其它进程被GDB挂起。

       查看调试的进程:info  inferiors

       切换当前调试的进程: inferios id

       使进程脱离GDB调试:defach inferiors id

gdb 8版本的好像多进程调试有问题


exec函数族          

       exec并没有创建新的进程,主要更换用区中的数据,内核区几乎没有变化。进程还是那个进程。

                     

 

      exec函数族的作用是根据指定的文件名找到可执行文件,并用它来替换调用进程的内容,换句话说,就是子啊调用进程内部执行一个可执行文件。

       exec函数族的函数执行成功后是不会有返回值的,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息,仍保持一样。所以就算我们判断成功也毫无意义,因为那条if不存在了。所以通常我们直接在exec函数调用后直接perror和exit,无需if

       失败返回-1

       exec函数族的一般规律:

       l(list)    命令行参数列表

       p(path)  搜索file时使用path

       v           使用命令行参数数组

       e          使用环境变量数组

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 encp[]);
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[]);
int execrpe(const char *file,char *const arg[],char *const envp[]);

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

            -参数

                       path:需要指定执行的文件路径或者名称

                       arg:可执行文件所需的命令行参数列表,第一个参数一般没有什么作用,为了方便写的是执行的程序名字,从第二个参数开始往后,就是程序执行所需的参数列表。参数列表最后需要以NULL结束(哨兵)。

             -返回值

                       只有当调用失败,才会有返回值,返回-1,并且设置error。

                       如果调用成功,没有返回值。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
    //创建一个子进程
    pid_t pid = fork();
    if(pid == -1){
        perror("fork");
        exit(0);
    }
    if(pid > 0){//父进程
        printf("I am parent,id:%d\n",getpid());
    }else if(pid == 0){//子进程
        printf("I am child,id:%d\n",getpid());
        execl("/home/chen/MyWorkingSpace/exec_test/b","b",NULL);//规范:写绝对路径,避免切换了工作目录就无法运行
        perror("execl");
        exit(0);
    }
    return 0;
}

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

               -会在path中查找,通常用来执行shell命令

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
    pid_t pid = fork();
    if(pid == -1){
        return 0;
    }
    if(pid > 0){
        printf("I am parent,id:%d\n",getpid());
    }else if(pid == 0){
        printf("I am child,id:%d\n",getpid());
        execlp("ls","ls","-la",NULL);
    }
    return 0;
}

     其它几个基本用不上,用法都差不多。


进程退出

#include <stdlib.h>
void exit(int status);
==================================================
#include <unistd.h>
void _exit(int status);
==================================================
status:是进程退出时的一个状态信息。父进程回收子进程资源的时候可以获取到(witpid)

一个时系统函数       一个是C库函数

                         

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
6月前
|
存储 Linux C语言
Linux进程等待
Linux进程等待
|
6月前
|
算法 Linux 调度
一文初识Linux进程(超详细!)
一文初识Linux进程(超详细!)
|
5月前
|
Linux 调度
Linux进程——Linux进程与进程优先级
Linux进程——Linux进程与进程优先级
35 1
|
4月前
|
缓存 Linux 调度
【linux】进程控制——进程创建,进程退出,进程等待
【linux】进程控制——进程创建,进程退出,进程等待
|
6月前
|
Linux Shell 调度
【linux进程(四)】僵尸进程和孤儿进程概念&进程优先级讲解
【linux进程(四)】僵尸进程和孤儿进程概念&进程优先级讲解
|
6月前
|
关系型数据库 MySQL Linux
Linux进程详解
Linux进程详解
78 0
|
NoSQL Linux Shell
Linux进程理解【进程状态】
Liunx进程状态详细讲解,包括运行、睡眠、暂停、死亡等状态讲解,以及僵尸进程、孤儿进程的介绍,干货满满!
315 0
Linux进程理解【进程状态】
|
6月前
|
Linux 程序员 C语言
4.3、Linux进程(2)
4.3、Linux进程(2)
47 0
|
6月前
|
存储 Linux 调度
4.2、Linux进程(1)
4.2、Linux进程(1)
52 0
|
存储 缓存 安全
Linux进程理解【进程认识】
Linux进程概念理解与创建操作详细讲解,干货满满!
4363 2
Linux进程理解【进程认识】