linux学习:进程

目录

例子1    获取当前进程的进程标识符

例子2   创建一个新的子进程

例子3    展示了父进程和子进程的进程标识符

例子4     区分父进程和子进程

例子5     区分父进程和子进程的行为

例子6    比较进程标识符来区分父进程和子进程

例子7     子进程如何修改一个变量,对父进程的影响

例子8     子进程创建并循环执行,每隔3秒打印一条消息

例子9    父进程和子进程分别进入循环,每隔一秒打印一条消息

例子10     vfork()创建子进程

例子11    vfork() 创建子进程,执行一定次数后会自行退出

例子12    

例子13    创建子进程,父子不同的操作。子在一定条件下会自行退出

例子14    父等待子进程结束后继续执行

例子15    wait() 父等待子结束,父打印子进程的退出状态

例子16   waitpid替代 wait检查子状态,waitpid允许父进程进行非阻塞检查或等待特定的子进程结束

例子17   子在一定条件下会自行退出,而父进程仅打印一次信息

例子18   execl执行一个新的程序。这个函数属于 exec 系列函数,它们用于替换当前进程的映像

例子19    execl函数来执行 /bin/ls 命令,通常用于列出目录内容。如果 execl() 调用成功,则不会返回;如果调用失败,会返回 -1 并打印错误信息

例子20    execl函数来执行 /bin/ls 命令,配上 -l 选项来以长格式列出目录内容。如果 execl() 调用成功,则会替换当前进程的映像并不返回;如果失败,会返回 -1 并打印错误信息

例子21    execl()执行系统命令 /bin/date,该命令用于显示当前系统日期和时间。如果 execl()调用成功,它会替换当前进程的映像为 /bin/date 并不返回;如果失败,会返回 -1 并打印错误信息

例子22    execlp()执行系统命令 ps,该命令用于显示当前系统的进程状态。如果 execlp() 调用成功,它会替换当前进程的映像为 ps 命令并不返回;如果失败,会返回 -1 并打印错误信息

例子23    execvp()执行系统命令 ps,用于显示当前系统的进程状态。execvp() 类似于 execlp(),但允许通过一个参数数组来传递命令和其参数。如果 execvp() 调用成功,它会替换当前进程的映像为 ps 命令并不返回;如果失败,会返回 -1 并打印错误信息

例子24    execv()执行 /bin/ps 命令,该命令用于显示当前系统的进程状态。execv() 类似于 execl(),但使用数组来传递命令和其参数。如果 execv() 调用成功,它会替换当前进程的映像为 /bin/ps 并不返回;如果失败,会返回 -1 并打印错误信息

例子25  输入来触发文件操作,在子进程中进行。这个例子特别是在输入为 1 时,程序会修改配置文件 config.txt 中的一个特定值

例子26    根据输入决定是否创建一个子进程来执行特定的程序 (changData),该程序预期对 config.txt 文件进行修改。如果输入不是 1,则打印消息并继续等待

例子27    根据输入决定执行一个外部命令(changData),该命令与配置文件 config.txt 相关联。如果用户输入 1,程序会创建一个子进程,该子进程通过 system 函数调用外部程序 ./changData 来处理文件 config.txt

例子28    system() 执行系统命令(在本例中是 ps),这个命令用来列出当前运行的进程。然而,该代码示例存在一个问题:它没有捕获 system() 调用的输出

例子29   popen() 和 fread() 函数从 ps 命令获取输出并将其读入到一个字符数组中。该程序展示了如何执行系统命令并捕获其输出,适合需要从命令行工具中直接读取数据的场景

例子30   处理命令行参数。它通过遍历 argc 和 argv 数组来打印所有传递给程序的参数

例子31   等待用户输入。当用户输入 1 时,程序会创建一个子进程,这个子进程会修改一个文件(./file1)中特定字符串("LENG=")之后的字符

例子32    execl()函数是为了替换当前的进程映像为/bin/ls命令的进程映像

例子33   vfork()创建子进程并控制执行顺序


例子1    获取当前进程的进程标识符

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid()

int main()
{
    pid_t pid;  // 定义进程标识符变量

    pid = getpid(); // 调用getpid()函数获取当前进程的PID

    printf("my pid is %d\n", pid);  // 打印当前进程的PID

    while(1);  // 使程序进入无限循环,防止程序立即结束

    return 0;  // 正常情况下,程序不会执行到这里,因为上面有无限循环
}

例子2   创建一个新的子进程

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid;  // 定义进程标识符变量

    pid = getpid(); // 调用getpid()函数获取当前进程的PID

    fork(); // 调用fork()创建一个子进程。父进程中,fork返回新创建的子进程的PID;在子进程中,fork返回0。

    printf("my pid is %d\n", pid);  // 打印当前进程的PID,注意这里将在父进程和子进程中都执行。

    return 0;  // 结束程序,父进程和子进程都会到达这里并退出
}

例子3    展示了父进程和子进程的进程标识符

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid;  // 定义进程标识符变量

    pid = getpid(); // 调用getpid()函数获取当前进程的PID

    fork(); // 调用fork()创建一个子进程。父进程中,fork返回新创建的子进程的PID;在子进程中,fork返回0。

    // 在printf中显示初始进程的PID和当前进程的PID。
    // "pid" 是调用fork()之前的父进程的PID。
    // "getpid()" 将获取当前进程的PID,这在父进程和子进程中可能不同。
    printf("my pid is %d, current pro id:%d\n", pid, getpid());

    return 0;  // 结束程序,父进程和子进程都会到达这里并退出
}

例子4     区分父进程和子进程

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid;  // 定义一个pid_t类型变量来存储原始进程的PID
    pid_t pid2; // 定义另一个pid_t类型变量来存储fork后的进程PID

    pid = getpid(); // 获取并存储当前进程的PID
    printf("before fork: pid = %d\n", pid); // 打印fork前的PID

    fork(); // 创建一个新的子进程。父进程中,fork返回子进程的PID;子进程中,fork返回0。

    pid2 = getpid(); // 获取fork后当前进程的PID
    printf("after fork: pid = %d\n", pid2); // 打印fork后的PID

    if(pid == pid2)
    {
        // 如果fork前后的PID相同,说明代码在父进程中执行
        printf("this is father print\n");
    }
    else
    {
        // 如果fork前后的PID不同,说明代码在子进程中执行
        printf("this is child print, child pid = %d\n", getpid());
    }

    return 0;  // 程序结束,父子进程都将执行到此处并退出
}

例子5     区分父进程和子进程的行为

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值

    // 打印当前进程的PID,此时为父进程
    printf("father: id=%d\n", getpid());

    pid = fork(); // 调用fork()来创建一个新的子进程

    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 在父进程中,fork()返回创建的子进程的PID
        printf("this is father print, pid = %d\n", getpid());
    }
    
    else if (pid == 0)
    {
        // 如果pid等于0,说明是子进程执行的代码块
        // 在子进程中,fork()返回0
        printf("this is child print, child pid = %d\n", getpid());
    }

    // 两个进程都将执行到这里,并结束程序
    return 0;
}

例子6    比较进程标识符来区分父进程和子进程

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid;     // 定义一个pid_t类型变量来存储原始进程的PID
    pid_t pid2;    // 定义另一个pid_t类型变量来存储fork后的进程PID
    pid_t retpid;  // 定义一个pid_t类型变量来存储fork()的返回值

    pid = getpid(); // 获取并存储当前进程的PID
    printf("before fork: pid = %d\n", pid); // 打印fork前的PID

    retpid = fork(); // 创建一个新的子进程。父进程中,fork返回子进程的PID;在子进程中,fork返回0。

    pid2 = getpid(); // 获取fork后当前进程的PID
    printf("after fork: pid = %d\n", pid2); // 打印fork后的PID

    if(pid == pid2)
    {
        // 如果fork前后的PID相同,说明代码在父进程中执行
        // retpid将是子进程的PID,因为在父进程中,fork()返回子进程的PID
        printf("this is father print: iretpid = %d\n", retpid);
    }
    else
    {
        // 如果fork前后的PID不同,说明代码在子进程中执行
        // 在子进程中,fork()返回0
        printf("this is child print, retpid=%d, child pid = %d\n", retpid, getpid());
    }

    return 0;  // 程序结束,父子进程都将执行到此处并退出
}

例子7     子进程如何修改一个变量,对父进程的影响

#include <stdio.h>   // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid() 和 fork()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int data = 10; // 定义一个整型变量data,并初始化为10

    // 打印当前进程的PID,此时为父进程
    printf("father: id=%d\n", getpid());

    pid = fork(); // 调用fork()来创建一个新的子进程

    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 在父进程中,fork()返回创建的子进程的PID
        printf("this is father print, pid = %d\n", getpid());
    }
    
    else if (pid == 0)
    {
        // 如果pid等于0,说明是子进程执行的代码块
        // 在子进程中,fork()返回0
        printf("this is child print, child pid = %d\n", getpid());

        // 子进程中修改data变量
        data = data + 100; // 将data加100
    }

    // 在父进程和子进程中都将执行到这里,并打印data的值
    printf("data=%d\n", data);

    return 0; // 程序结束,父子进程都将执行到此处并退出
}

例子8     子进程创建并循环执行,每隔3秒打印一条消息

#include <stdio.h>   // 引入标准输入输出库,用于打印和输入
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int data = 10; // 定义一个整型变量data,并初始化为10

    while(1) { // 无限循环

        printf("please input a data\n"); // 提示用户输入数据
        scanf("%d", &data); // 从标准输入读取一个整数到变量data

        if(data == 1) { // 如果输入的数据是1
            
            pid = fork(); // 调用fork()来创建一个新的子进程
            
            if(pid > 0) {
                // 如果pid大于0,说明是父进程执行的代码块
                // 父进程不进行任何操作,继续循环等待新的输入
            }
            else if(pid == 0) {
                // 如果pid等于0,说明是子进程执行的代码块
                // 在子进程中,fork()返回0
                while(1) {
                    // 子进程进入另一个无限循环,每隔3秒打印一次消息
                    printf("do net request, pid=%d\n", getpid()); // 打印当前子进程的PID
                    sleep(3); // 使子进程休眠3秒
                }
            }
        }
        else {
            // 如果输入的数据不是1
            printf("wait, do nothing\n"); // 打印等待信息
        }
    }

    return 0; // 程序理论上不会到达这里,因为有无限循环
}

例子9    父进程和子进程分别进入循环,每隔一秒打印一条消息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 父进程进入一个无限循环
        while (1) {
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度,防止过快消耗系统资源
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒,同样为了减缓循环速度
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子10     vfork()创建子进程

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), vfork() 和 sleep()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储vfork()的返回值

    pid = vfork(); // 调用vfork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 父进程进入一个无限循环
        while (1) {
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度,防止过快消耗系统资源
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒,同样为了减缓循环速度
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子11    vfork() 创建子进程,执行一定次数后会自行退出

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), vfork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储vfork()的返回值
    int cnt = 0; // 定义并初始化计数器

    pid = vfork(); // 调用vfork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 父进程进入一个无限循环
        while (1) {
            printf("cnt=%d\n", cnt); // 打印父进程中的计数器值
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 3) {
                exit(0); // 当计数器达到3时,子进程退出
                break;   // 退出循环
            }
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子12    

例子13    创建子进程,父子不同的操作。子在一定条件下会自行退出

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int cnt = 0; // 定义并初始化计数器

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        // 父进程进入一个无限循环
        while (1) {
            printf("cnt=%d\n", cnt); // 打印父进程中的计数器值
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 5) {
                exit(0); // 当计数器达到5时,子进程退出
            }
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子14    父等待子进程结束后继续执行

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()
#include <sys/wait.h>  // 引入 wait() 函数需要的库

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int cnt = 0; // 定义并初始化计数器

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        wait(NULL);  // 父进程等待任何子进程结束,这里不关心子进程的退出状态
        // 父进程在子进程结束后继续执行
        while (1) {
            printf("cnt=%d\n", cnt); // 打印父进程中的计数器值
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 5) {
                exit(0); // 当计数器达到5时,子进程退出
            }
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子15    wait() 父等待子结束,父打印子进程的退出状态

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()
#include <sys/wait.h>  // 引入 wait() 函数所需的库

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int cnt = 0; // 定义并初始化计数器
    int status = 10; // 定义一个整数来存储子进程的退出状态

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        wait(&status); // 父进程等待子进程结束,并通过status变量获取子进程的退出状态
        printf("child quit, child status = %d\n", WEXITSTATUS(status)); // 打印子进程的退出状态

        // 父进程在子进程结束后继续执行
        while (1) {
            printf("cnt=%d\n", cnt); // 打印父进程中的计数器值
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 5) {
                exit(3); // 当计数器达到5时,子进程退出并返回状态码3
            }
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子16   waitpid替代 wait检查子状态,waitpid允许父进程进行非阻塞检查或等待特定的子进程结束

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), fork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()
#include <sys/wait.h>  // 引入等待函数库,用于 waitpid()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int cnt = 0; // 定义并初始化计数器
    int status = 10; // 定义一个整数来存储子进程的退出状态

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        waitpid(pid, &status, WNOHANG); // 使用非阻塞方式等待子进程结束,不会挂起父进程
        printf("child quit, child status = %d\n", WEXITSTATUS(status)); // 打印子进程的退出状态

        // 父进程在子进程结束后继续执行
        while (1) {
            printf("cnt=%d\n", cnt); // 打印父进程中的计数器值
            printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
            sleep(1); // 父进程休眠1秒,减缓循环速度
        }    
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环
        while (1) {
            printf("this is child print, pid = %d\n", getpid()); // 打印子进程的信息和PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 5) {
                exit(3); // 当计数器达到5时,子进程退出并返回状态码3
            }
        }    
    }

    return 0; // 程序理论上不会到达这里,因为父子进程都被设置在无限循环中
}

例子17   子在一定条件下会自行退出,而父进程仅打印一次信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <sys/types.h> // 引入数据类型,包括 pid_t
#include <unistd.h>    // 引入 POSIX 操作系统API,包括 getpid(), getppid(), fork() 和 sleep()
#include <stdlib.h>    // 引入标准库,用于 exit()

int main()
{
    pid_t pid; // 定义一个pid_t类型变量来存储fork()的返回值
    int cnt = 0; // 定义并初始化计数器

    pid = fork(); // 调用fork()来创建一个新的子进程
    
    if (pid > 0)
    {
        // 如果pid大于0,说明是父进程执行的代码块
        printf("this is father print, pid = %d\n", getpid()); // 打印父进程的信息和PID
        // 父进程没有进入循环,将直接结束执行
    }
    
    else if (pid == 0) {
        // 如果pid等于0,说明是子进程执行的代码块
        // 子进程进入一个无限循环,直到cnt等于5
        while (1) {
            printf("this is child print, pid = %d, my father pid=%d\n", getpid(), getppid()); // 打印子进程的信息和其父进程的PID
            sleep(1); // 子进程休眠1秒
            cnt++; // 子进程中的计数器增加
            if (cnt == 5) {
                exit(3); // 当计数器达到5时,子进程退出并返回状态码3
            }
        }    
    }

    return 0; // 父进程执行完打印后将结束,子进程在计数达到5后也将结束
}

例子18   execl执行一个新的程序。这个函数属于 exec 系列函数,它们用于替换当前进程的映像

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execl()

// 函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    // 打印 "before execl" 表明当前在调用 execl 前
    printf("before execl\n");

    // 调用 execl 执行名为 "echoarg" 的程序,该程序应在当前目录下
    // "echoarg" 是被执行程序的名称,同时也是传递给该程序的第一个参数
    // "abc" 是传递给 echoarg 程序的第二个参数
    // NULL 表示参数列表的结束
    if(execl("./echoarg", "echoarg", "abc", NULL) == -1)
    {
        // 如果 execl 返回 -1,则表示执行失败
        printf("execl failed!\n");

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execl 成功时不返回,如果程序执行到这里说明 execl 调用失败
    printf("after execl\n");

    return 0;
}

例子19    execl函数来执行 /bin/ls 命令,通常用于列出目录内容。如果 execl() 调用成功,则不会返回;如果调用失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execl()

// 函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    // 打印 "before execl" 表明当前在调用 execl 前
    printf("before execl\n");

    // 调用 execl 执行位于 "/bin/ls" 的程序,通常用于列出目录内容
    // "ls" 是传递给 /bin/ls 程序的参数,用于识别程序名称
    // NULL 指示参数列表的结束
    if(execl("/bin/ls", "ls", NULL, NULL) == -1)
    {
        // 如果 execl 返回 -1,则表示执行失败
        printf("execl failed!\n");      

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execl 成功时不返回,如果程序执行到这里说明 execl 调用失败
    printf("after execl\n");

    return 0;
}

例子20    execl函数来执行 /bin/ls 命令,配上 -l 选项来以长格式列出目录内容。如果 execl() 调用成功,则会替换当前进程的映像并不返回;如果失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execl()

// 函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    // 打印 "before execl" 表明当前在调用 execl 前
    printf("before execl\n");

    // 调用 execl 执行位于 "/bin/ls" 的程序,使用 "-l" 选项来以详细列表格式列出文件和目录
    // "ls" 是传递给 /bin/ls 程序的参数,用于识别程序名称
    // "-l" 是一个参数,告诉 ls 命令以长格式显示信息
    // NULL 指示参数列表的结束
    if(execl("/bin/ls", "ls", "-l", NULL) == -1)
    {
        // 如果 execl 返回 -1,则表示执行失败
        printf("execl failed!\n");      

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execl 成功时不返回,如果程序执行到这里说明 execl 调用失败
    printf("after execl\n");

    return 0;
}

例子21    execl()执行系统命令 /bin/date,该命令用于显示当前系统日期和时间。如果 execl()调用成功,它会替换当前进程的映像为 /bin/date 并不返回;如果失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execl()

// 函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    // 打印信息,表明程序意图获取当前系统日期
    printf("this pro get system date:\n");

    // 调用 execl 执行位于 "/bin/date" 的系统命令
    // "date" 是传递给 /bin/date 程序的参数,用于识别程序名称
    // NULL 指示参数列表的结束
    if(execl("/bin/date", "date", NULL, NULL) == -1)
    {
        // 如果 execl 返回 -1,则表示执行失败
        printf("execl failed!\n");      

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execl 成功时不返回,如果程序执行到这里说明 execl 调用失败
    printf("after execl\n");

    return 0;
}

例子22    execlp()执行系统命令 ps,该命令用于显示当前系统的进程状态。如果 execlp() 调用成功,它会替换当前进程的映像为 ps 命令并不返回;如果失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execlp()

// 函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    // 打印信息,表明程序意图获取当前系统进程状态
    printf("this pro get system date:\n");

    // 调用 execlp 执行系统命令 "ps"
    // "ps" 是传递给 execlp() 的参数,用于执行 ps 命令显示进程信息
    // NULL 指示参数列表的结束
    if(execlp("ps", "ps", NULL, NULL) == -1)
    {
        // 如果 execlp 返回 -1,则表示执行失败
        printf("execl failed!\n");

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execlp 成功时不返回,如果程序执行到这里说明 execlp 调用失败
    printf("after execl\n");

    return 0;
}

例子23    execvp()执行系统命令 ps,用于显示当前系统的进程状态。execvp() 类似于 execlp(),但允许通过一个参数数组来传递命令和其参数。如果 execvp() 调用成功,它会替换当前进程的映像为 ps 命令并不返回;如果失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execvp()

// 函数原型:int execvp(const char *file, char *const argv[]);

int main(void)
{
    // 打印信息,表明程序意图获取当前系统进程状态
    printf("this pro get system date:\n");

    // 创建参数数组,用于传递给 execvp()
    char *argv[] = {"ps", NULL, NULL};

    // 调用 execvp 执行系统命令 "ps"
    // "ps" 是传递给 execvp() 的命令,argv 是命令和参数的数组
    if(execvp("ps", argv) == -1)
    {
        // 如果 execvp 返回 -1,则表示执行失败
        printf("execl failed!\n");      

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execvp 成功时不返回,如果程序执行到这里说明 execvp 调用失败
    printf("after execl\n");

    return 0;
}

例子24    execv()执行 /bin/ps 命令,该命令用于显示当前系统的进程状态。execv() 类似于 execl(),但使用数组来传递命令和其参数。如果 execv() 调用成功,它会替换当前进程的映像为 /bin/ps 并不返回;如果失败,会返回 -1 并打印错误信息

#include <stdio.h>    // 引入标准输入输出库,用于打印
#include <stdlib.h>   // 引入标准库,用于标准库功能
#include <unistd.h>   // 引入 POSIX 操作系统API,包括 execv()

// 函数原型:int execv(const char *path, char *const argv[]);

int main(void)
{
    // 打印信息,表明程序意图获取当前系统进程状态
    printf("this pro get system date:\n");

    // 创建参数数组,用于传递给 execv()
    char *argv[] = {"ps", NULL, NULL};  // "ps" 是命令名,NULL 表示参数列表结束

    // 调用 execv 执行位于 "/bin/ps" 的系统命令
    // "/bin/ps" 是命令的完整路径
    // argv 是命令和参数的数组
    if(execv("/bin/ps", argv) == -1)
    {
        // 如果 execv 返回 -1,则表示执行失败
        printf("execl failed!\n");

        // perror 用于打印上一个函数调用的错误描述(基于全局的 errno 变量)
        perror("why");
    }

    // 由于 execv 成功时不返回,如果程序执行到这里说明 execv 调用失败
    printf("after execl\n");

    return 0;
}

例子25  输入来触发文件操作,在子进程中进行。这个例子特别是在输入为 1 时,程序会修改配置文件 config.txt 中的一个特定值

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    pid_t pid;  // 用于存储进程ID
    int data = 10;  // 用户输入的数据初始化为10

    while(1) {  // 无限循环,直到程序被外部方式终止

        printf("please input a data\n");  // 提示用户输入数据
        scanf("%d", &data);  // 读取用户输入的整数数据

        if(data == 1) {  // 如果输入的数据为1,则进行文件操作
            int fdSrc;  // 文件描述符

            pid = fork();  // 创建子进程
            if(pid > 0) {
                wait(NULL);  // 父进程等待子进程完成
            }

            if(pid == 0) {  // 子进程代码块
                char *readBuf = NULL;  // 读缓冲区指针

                fdSrc = open("config.txt", O_RDWR);  // 打开文件config.txt进行读写操作
                int size = lseek(fdSrc, 0, SEEK_END);  // 获取文件大小
                lseek(fdSrc, 0, SEEK_SET);  // 将文件指针重置到文件开始

                readBuf = (char *)malloc(sizeof(char) * size + 8);  // 分配读缓冲区内存

                int n_read = read(fdSrc, readBuf, size);  // 读取文件内容到缓冲区

                char *p = strstr(readBuf, "LENG=");  // 在缓冲区中查找"LENG="
                if(p == NULL) {
                    printf("not found\n");  // 如果没有找到,打印未找到并退出
                    exit(-1);
                }

                p = p + strlen("LENG=");  // 移动指针到"LENG="之后
                *p = '5';  // 修改内容

                lseek(fdSrc, 0, SEEK_SET);  // 将文件指针重置到文件开始
                int n_write = write(fdSrc, readBuf, strlen(readBuf));  // 将修改后的缓冲区内容写回文件
                
                close(fdSrc);  // 关闭文件
                exit(0);  // 子进程退出
            }

        } else {
            printf("wait, do nothing\n");  // 如果输入不是1,提示等待
        }
    }

    return 0;  // 主程序循环,实际上这里永远不会执行到
}

例子26    根据输入决定是否创建一个子进程来执行特定的程序 (changData),该程序预期对 config.txt 文件进行修改。如果输入不是 1,则打印消息并继续等待

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    pid_t pid;  // 用于存储进程ID
    int data = 10;  // 用户输入的数据初始化为10

    while(1) {  // 无限循环,直到程序被外部方式终止

        printf("please input a data\n");  // 提示用户输入数据
        scanf("%d", &data);  // 读取用户输入的整数数据

        if(data == 1) {  // 如果输入的数据为1,则进行操作
            int fdSrc;  // 文件描述符,虽然在此代码版本中未被使用
            
            pid = fork();  // 创建子进程
            if(pid > 0) {
                wait(NULL);  // 父进程等待子进程完成
            }
                    
            if(pid == 0) {  // 子进程代码块
                // 子进程执行一个外部程序 "changData"
                // 第一个参数 "./changData" 指定了程序的路径
                // 第二个参数 "changData" 是传递给程序的 argv[0],即程序名
                // 第三个参数 "config.txt" 是传递给 changData 程序的参数,指定要操作的文件
                // NULL 表示参数列表的结束
                execl("./changData", "changData", "config.txt", NULL);

                // 如果execl执行失败,则会继续执行以下代码
                perror("Failed to execute changData");  // 打印执行失败的原因
                exit(1);  // 子进程失败时退出
            }

        } else {
            printf("wait, do nothing\n");  // 如果输入不是1,提示等待并什么也不做
        }
    }

    return 0;  // 主程序循环,实际上这里永远不会执行到
}

例子27    根据输入决定执行一个外部命令(changData),该命令与配置文件 config.txt 相关联。如果用户输入 1,程序会创建一个子进程,该子进程通过 system 函数调用外部程序 ./changData 来处理文件 config.txt

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>


int main()
{
    pid_t pid;  // 用于存储进程ID
    int data = 10;  // 用户输入的数据初始化为10

    while(1) {  // 无限循环,直到程序被外部方式终止

        printf("please input a data\n");  // 提示用户输入数据
        scanf("%d", &data);  // 读取用户输入的整数数据

        if(data == 1) {  // 如果输入的数据为1,则进行操作
            int fdSrc;  // 文件描述符,虽然在此代码版本中未被使用
            
            pid = fork();  // 创建子进程
            if(pid > 0) {
                wait(NULL);  // 父进程等待子进程完成
            }
                    
            if(pid == 0) {  // 子进程代码块
                // 子进程执行一个外部程序 "changData" 通过 system 函数
                // "./changData config.txt" 是被调用的命令,传递 'config.txt' 作为参数
                system("./changData config.txt");

                exit(0);  // 执行完外部命令后,子进程应正常退出
            }

        } else {
            printf("wait, do nothing\n");  // 如果输入不是1,提示等待并什么也不做
        }
    }

    return 0;  // 主程序循环,实际上这里永远不会执行到
}

例子28    system() 执行系统命令(在本例中是 ps),这个命令用来列出当前运行的进程。然而,该代码示例存在一个问题:它没有捕获 system() 调用的输出

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    char ret[1024] = {0};  // 定义一个字符数组并初始化所有元素为0,用于存储输出

    system("ps");  // 调用system函数执行ps命令,ps命令用于显示当前运行的进程
    // 注意:system函数实际上不会将命令输出存储在ret数组中
    printf("ret=%s\n", ret);  // 打印ret数组的内容,预期输出为空字符串,因为system不会更改ret
    
    return 0;
}

例子29   popen() 和 fread() 函数从 ps 命令获取输出并将其读入到一个字符数组中。该程序展示了如何执行系统命令并捕获其输出,适合需要从命令行工具中直接读取数据的场景

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 函数原型:int execl(const char *path, const char *arg, ...);
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

int main(void)
{
    char ret[1024] = {0};  // 创建字符数组,用于存储命令输出
    FILE *fp;  // 文件指针,用于执行 popen() 返回的流

    // 执行 'ps' 命令,并打开用于读取的管道
    fp = popen("ps", "r");
    if (fp == NULL) {  // 检查 popen 是否成功打开了管道
        perror("Failed to run command");  // 打印 popen 执行失败的错误详情
        exit(1);  // 退出程序
    }

    // 从 'ps' 命令的输出流中读取数据到 ret 数组中
    int nread = fread(ret, 1, 1024, fp);  // 读取最多 1024 个字节到 ret

    // 打印读取的字节数和内容
    printf("read ret %d byte, ret=%s\n", nread, ret);

    // 关闭管道
    pclose(fp);

    return 0;
}

例子30   处理命令行参数。它通过遍历 argc 和 argv 数组来打印所有传递给程序的参数

#include <stdio.h>

int main(int argc, char *argv[])  // main 函数接受命令行参数:argc 表示参数数量,argv 是参数字符串数组
{
    int i = 0;  // 定义循环计数变量

    for(i = 0; i < argc; i++)  // 循环遍历所有命令行参数
    {
        printf("argv[%d]: %s\n", i, argv[i]);  // 打印每个参数的索引和内容
    }

    return 0;  // 程序正常结束
}

例子31   等待用户输入。当用户输入 1 时,程序会创建一个子进程,这个子进程会修改一个文件(./file1)中特定字符串("LENG=")之后的字符

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    pid_t pid;  // 用于存储进程ID
    int data = 10;  // 用户输入的数据初始化为10

    while(1) {  // 无限循环,直到程序被外部方式终止

        printf("please input a data\n");  // 提示用户输入数据
        scanf("%d", &data);  // 读取用户输入的整数数据

        if(data == 1) {  // 如果输入的数据为1,则进行操作
            
            pid = fork();  // 创建子进程
            
            if(pid > 0) {
                // 父进程执行的代码块
                // 父进程不做任何操作,继续循环等待下一次输入
            }
            else if(pid == 0) {
                // 子进程执行的代码块
                char *readBuf = NULL;  // 读缓冲区指针
                int fdSrc;  // 文件描述符

                fdSrc = open("./file1", O_RDWR);  // 打开文件file1进行读写操作
                int size = lseek(fdSrc, 0, SEEK_END);  // 获取文件大小
                lseek(fdSrc, 0, SEEK_SET);  // 将文件指针重置到文件开始

                readBuf = (char *)malloc(sizeof(char) * size + 8);  // 分配读缓冲区内存

                int n_read = read(fdSrc, readBuf, size);  // 读取文件内容到缓冲区

                char *p = strstr(readBuf, "LENG=");  // 在缓冲区中查找"LENG="
                if(p == NULL) {
                    printf("not found\n");  // 如果没有找到,打印未找到并退出
                    exit(-1);
                }

                p += strlen("LENG=");  // 移动指针到"LENG="之后
                *p = '5';  // 修改内容

                lseek(fdSrc, 0, SEEK_SET);  // 将文件指针重置到文件开始
                int n_write = write(fdSrc, readBuf, strlen(readBuf));  // 将修改后的缓冲区内容写回文件
                
                close(fdSrc);  // 关闭文件
                free(readBuf); // 释放分配的内存
                exit(0);  // 子进程退出
            }
        }
        else {
            printf("wait, do nothing\n");  // 如果输入不是1,提示等待并什么也不做
        }
    }

    return 0;  // 主程序循环,实际上这里永远不会执行到
}

例子32    execl()函数是为了替换当前的进程映像为/bin/ls命令的进程映像

#include <stdio.h>   // 引入标准输入输出头文件。
#include <stdlib.h>  // 引入标准库头文件,用于执行一些常规的函数操作,比如exit()。
#include <unistd.h>  // 引入UNIX标准函数定义头文件,提供对POSIX操作系统API的访问。

// 函数原型声明:int execl(const char *path, const char *arg, ...);
// execl()函数用于执行指定的文件路径,将当前进程替换为一个新的进程。

int main(void)
{
    printf("before execl\n");  // 在调用execl()前打印信息。

    // 调用execl()尝试执行/bin/ls命令来列出目录内容。"ls"是命令名,"-l"是参数。
    if(execl("/bin/ls", "ls", "-l", NULL) == -1)
    {
        printf("execl failed!\n");  // 如果execl()返回-1,则说明执行失败。

        perror("why");  // 使用perror()函数输出错误原因。perror()会根据全局错误码errno输出错误描述。
    }

    // 如果execl()函数调用成功,则不会执行以下代码,因为execl()会替换当前进程的映像,不会返回。
    printf("after execl\n");  // 如果execl()失败,会继续执行这里的代码。
    return 0;  // 程序正常结束。
}

例子33   vfork()创建子进程并控制执行顺序

#include <stdio.h>   // 引入标准输入输出头文件,用于printf函数。
#include <sys/types.h>  // 引入数据类型定义,例如pid_t。
#include <unistd.h>  // 引入POSIX操作系统API,例如vfork和getpid函数。
#include <stdlib.h>  // 引入标准库头文件,用于执行一些常规的函数操作,如exit()。

int main()
{
    pid_t pid;  // 声明一个pid_t类型的变量来存储进程ID。

    int cnt = 0;  // 计数器,用于子进程中记录循环次数。
    
    pid = vfork();  // 创建一个子进程,子进程与父进程共享内存空间。
    
    if (pid > 0)  // 如果pid大于0,表示当前代码块在父进程中执行。
    {
        while (1) {  // 父进程无限循环。
            printf("cnt=%d\n", cnt);  // 打印当前计数器的值(注意:这个值在父进程中不会改变)。
            printf("this is father print, pid = %d\n", getpid());  // 打印父进程的ID。
            sleep(1);  // 暂停父进程1秒,以便观察输出。
        }   
    }
    
    else if (pid == 0) {  // 如果pid等于0,表示当前代码块在子进程中执行。
        while (1) {  // 子进程无限循环。
            printf("this is child print, pid = %d\n", getpid());  // 打印子进程的ID。
            sleep(1);  // 暂停子进程1秒,以便观察输出。
            cnt++;  // 子进程中计数器递增。
            if (cnt == 3) {  // 如果计数器达到3,子进程退出。
                exit(0);  // 调用exit函数,正常终止子进程。
            }
        }   
    }

    return 0;  // 主程序结束(通常这个部分不会被执行到,因为父子进程已经进入了无限循环)。
}

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值