LInux ---- 多进程


头文件
    #include <sys/types.h>
    #include <unistd.h>
函数 pid_t fork(void); 函数的作用:用于创建子进程。 返回值:类型:pid_t    fork()的返回值会返回两次。一次是在父进程中,一次是在子进程中。 在父进程中返回创建的子进程的ID, 在子进程中返回0
      在父进程中返回-1,表示创建子进程失败,并且设置errno
如何区分父进程和子进程:通过fork的返回值父子进程之间的关系: 区别: 1.fork()函数的返回值不同 父进程中: >0 返回的子进程的ID 子进程中: =0 2.pcb中的一些数据 当前进程的id pid 当前进程的父进程的id ppid 信号集 共同点: 某些状态下:子进程刚被创建出来,还没有执行任何的写数据的操作 - 用户区的数据 - 文件描述符表 父子进程对变量是不是共享的? - 刚开始的时候,是一样的,共享的。如果修改了数据,不共享了。 - 读时共享(子进程被创建,两个进程没有做任何写操作),写时拷贝。
复制代码
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <unistd.h>
 4 
 5 int main() {
 6 
 7     int num = 10;
 8 
 9     // 创建子进程
10     pid_t pid = fork();
11 
12     // 判断是父进程还是子进程
13     if (pid > 0) {
14         // printf("pid : %d\n", pid);
15         // 如果大于0,返回的是创建的子进程的进程号,当前是父进程
16         printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
17 
18         printf("parent num : %d\n", num);
19         num += 10;
20         printf("parent num += 10 : %d\n", num);
21     }
22     else if (pid == 0) {
23         // 当前是子进程
24         printf("i am child process, pid : %d, ppid : %d\n", getpid(), getppid());
25 
26         printf("child num : %d\n", num);
27         num += 100;
28         printf("child num += 100 : %d\n", num);
29     }
30 
31     // for循环
32     for (int i = 0; i < 3; i++) {
33         printf("i : %d , pid : %d\n", i, getpid());
34         sleep(1);
35     }
36 
37     return 0;
38 }
复制代码

 

  实际上,更准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。

  注意:fork之后父子进程共享文件,fork产生的子进程与父进程相同的文件文件描述符指向相同的文件表,引用计数增加,共享文件偏移指针。

 

GDB调试

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

  设置调试父进程或者子进程:set follow-fork-mode [parent(默认)| child]

  设置调试模式:set detach-on-fork [on | off]

    默认为 on,表示调试当前进程的时候,其它的进程继续运行,如果为 off,调试当前进程的时候,其它进程被 GDB 挂起。

  查看调试的进程:info inferiors

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

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

在父进程中返回-1,表示创建子进程失败,并且设置errno

exce函数族
  

  在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。

  例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,在利用exec系统调用将新产生的子进程完全替换成ps进程。

  exec 函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的 内容,换句话说,就是在调用进程内部执行一个可执行文件。
  exec 函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程 ID 等一些表面上的信息仍保持原样, 颇有些神似“三十六计”中的“金蝉脱壳”。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回-1,从原程序的调用点接着往下执行。
  int execl(const char *path, const char *arg, ...);
        - 参数:
            - path:需要指定执行文件的路径或者名称
                a.out   /home/nowcoder/a.out 推荐使用绝对路径
                ./a.out hello world
        - arg:是执行可执行文件所需要的参数列表
                第一个参数一般没有什么作用,为了方便,一般写的是执行的程序的名称
                从第二个参数开始往后,就是程序执行所需要的的参数列表。
                参数最后需要以NULL结束(哨兵)
        - 返回值:
            只有当调用失败,才会有返回值,返回-1,并且设置errno
            如果调用成功,没有返回值。
 
  int execlp(const char *file, const char *arg, ... );
        - 借助环境变量PATH加载一个进程,如果找到了就执行,找不到就执行不成功。
        - 参数:
            - file:需要执行的可执行文件的文件名
                a.out
                ps
            - arg:是执行可执行文件所需要的参数列表
                第一个参数一般没有什么作用,为了方便,一般写的是执行的程序的名称
                从第二个参数开始往后,就是程序执行所需要的的参数列表。
                参数最后需要以NULL结束(哨兵)
        - 返回值:
            只有当调用失败,才会有返回值,返回-1,并且设置errno
            如果调用成功,没有返回值。


        int execv(const char *path, char *const argv[]);
        argv是需要的参数的一个字符串数组
        char * argv[ ] = {"ps", "aux", NULL};
        execv("/bin/ps", argv);

        int execve(const char *filename, char *const argv[], char *const envp[]);
        char * envp[ ] = {"/home/nowcoder", "/home/bbb", "/home/aaa"};
 
复制代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {

    // 创建一个子进程,在子进程中执行exec函数族中的函数
    pid_t pid = fork();

    if (pid > 0) {
        // 父进程
        printf("i am parent process, pid : %d\n", getpid());
        sleep(1);
    }
    else if (pid == 0) {
        // 子进程
        execl("hello", "hello", NULL);     //第一个hello为相对路径,即当前文件夹内
        execlp("ps", "ps", "aux", NULL);    //环境变量中有ps的路径
        execl("/bin/ps", "ps", "aux", NULL);
        perror("execl");
        printf("i am child process, pid : %d\n", getpid());
    }

    for (int i = 0; i < 3; i++) {
        printf("i = %d, pid = %d\n", i, getpid());
    }

    return 0;
}
复制代码

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

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

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

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

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

  ◼ int execvpe(const char *file, char *const argv[], char *const envp[]);

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

 

 

  l(list) 参数地址列表,以空指针结尾

  v(vector) 存有各参数地址的指针数组的地址

  p(path) 按 PATH 环境变量指定的目录搜索可执行文件

  e(environment) 存有环境变量字符串地址的指针数组的地址

 


posted @   Happinesspill  阅读(165)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示