目录
例子11 vfork() 创建子进程,执行一定次数后会自行退出
例子13 创建子进程,父子不同的操作。子在一定条件下会自行退出
例子15 wait() 父等待子结束,父打印子进程的退出状态
例子16 waitpid替代 wait检查子状态,waitpid允许父进程进行非阻塞检查或等待特定的子进程结束
例子18 execl执行一个新的程序。这个函数属于 exec 系列函数,它们用于替换当前进程的映像
例子19 execl函数来执行 /bin/ls 命令,通常用于列出目录内容。如果 execl() 调用成功,则不会返回;如果调用失败,会返回 -1 并打印错误信息
例子20 execl函数来执行 /bin/ls 命令,配上 -l 选项来以长格式列出目录内容。如果 execl() 调用成功,则会替换当前进程的映像并不返回;如果失败,会返回 -1 并打印错误信息
例子22 execlp()执行系统命令 ps,该命令用于显示当前系统的进程状态。如果 execlp() 调用成功,它会替换当前进程的映像为 ps 命令并不返回;如果失败,会返回 -1 并打印错误信息
例子25 输入来触发文件操作,在子进程中进行。这个例子特别是在输入为 1 时,程序会修改配置文件 config.txt 中的一个特定值
例子26 根据输入决定是否创建一个子进程来执行特定的程序 (changData),该程序预期对 config.txt 文件进行修改。如果输入不是 1,则打印消息并继续等待
例子28 system() 执行系统命令(在本例中是 ps),这个命令用来列出当前运行的进程。然而,该代码示例存在一个问题:它没有捕获 system() 调用的输出
例子29 popen() 和 fread() 函数从 ps 命令获取输出并将其读入到一个字符数组中。该程序展示了如何执行系统命令并捕获其输出,适合需要从命令行工具中直接读取数据的场景
例子30 处理命令行参数。它通过遍历 argc 和 argv 数组来打印所有传递给程序的参数
例子31 等待用户输入。当用户输入 1 时,程序会创建一个子进程,这个子进程会修改一个文件(./file1)中特定字符串("LENG=")之后的字符
例子32 execl()函数是为了替换当前的进程映像为/bin/ls命令的进程映像
例子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; // 主程序结束(通常这个部分不会被执行到,因为父子进程已经进入了无限循环)。
}