进程-线程-练习:线程:5的平方和立方分开计算;子进程:ls指令执行

逻辑书写

请写下你的逻辑,与参考逻辑对比,如有缺失,可细看本文


完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>

// 线程函数:计算平方
void *square(void *arg) {
    int n = *(int *)arg;
    printf("线程1: %d的平方是%d\n", n, n * n);
    return NULL;
}

// 线程函数:计算立方
void *cube(void *arg) {
    int n = *(int *)arg;
    printf("线程2: %d的立方是%d\n", n, n * n * n);
    return NULL;
}

int main() {
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork失败");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {  // 子进程
        printf("子进程启动,执行命令:ls -l\n");
        execl("/bin/ls", "ls", "-l", NULL);  // 通过execl执行命令
        perror("execl失败");                 // 若execl执行失败才会执行到这里
        exit(EXIT_FAILURE);
    } else {               // 父进程
        printf("父进程创建子进程,子进程PID=%d\n", pid);

        // 创建两个线程
        pthread_t t1, t2;
        int num = 5;  // 传递给线程的参数

        pthread_create(&t1, NULL, square, &num);
        pthread_create(&t2, NULL, cube, &num);

        // 等待线程结束
        pthread_join(t1, NULL);
        pthread_join(t2, NULL);

        // 等待子进程结束
        waitpid(pid, NULL, 0);
        printf("父进程结束\n");
    }

    return 0;
}

代码解析

  1. 进程创建(fork

    • fork() 创建子进程。父进程获取子进程的 PID,子进程的 pid 为 0。
    • 子进程调用 execl 执行 /bin/ls -l 命令,替换自身代码为 ls 的代码。
    • 如果 execl 失败,子进程会打印错误并退出。
  2. 线程操作(pthread

    • 父进程创建两个线程 t1t2,分别执行 squarecube 函数。
    • 线程函数通过指针接收参数(这里是整数 num = 5)。
    • pthread_join 确保父进程等待线程结束再继续。
  3. 进程同步(waitpid

    • 父进程调用 waitpid 等待子进程结束,避免僵尸进程。

知识回顾

waitpid

回顾之前对fork讲解过程中,涉及到的PCB(近程控制块)。在这里再次补充并巩固,PCB中包含了该进程的各种状态信息,如是否正在运行、是否已结束、退出状态等。
在本次训练中 waitpid便是利用这些状态,完成等待判断!

execl

关于execl,我并没有找到很好的资料解读,因此我想通过本次训练来阐述execl的具体用法。
execl 函数是 UNIX 和类 UNIX 操作系统中用于执行一个新程序的系统调用。当你调用 execl 时,当前进程的镜像(即代码、数据和堆栈等)会被新程序的镜像所替换,但进程 ID(PID)保持不变。这意味着,从外部看,这个进程似乎运行了一个新的程序,但实际上它只是被新的程序覆盖了。
下面是 execl 函数的一个基本解释,特别是针对代码 execl(“/bin/ls”, “ls”, “-l”, NULL);:
函数原型

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

参数解释
path:要执行的程序的路径。在这个例子中,它是 “/bin/ls”,即列出目录内容的 ls 命令的绝对路径。
arg:传递给新程序的命令行参数。第一个参数 arg 通常是程序的名称(在这个例子中为 “ls”),它会被存储在新程序的 argv[0] 中。随后的参数是传递给程序的其他命令行参数(在这个例子中为 “-l”),最后一个参数必须是 (char *) NULL,用来标记参数列表的结束。
代码解释
execl(“/bin/ls”, “ls”, “-l”, NULL); 这行代码的作用是:

  • 替换当前进程的镜像为 “/bin/ls” 程序。
  • 将 “ls” 作为新程序的 argv[0],将 “-l” 作为 argv[1] 传递给新程序。
  • NULL 标记了参数列表的结束。
    当这行代码执行时,如果 execl 调用成功,那么当前进程将被 ls 程序替换,并且 ls 程序将以 -l 选项(长格式列出目录内容)运行。由于进程镜像被替换,因此当前进程中的后续代码将不会被执行。

如果 execl 调用失败,它将返回 -1,并且 errno 将被设置为一个错误码,指示失败的原因。但是,在大多数情况下,如果 execl 调用失败,程序将立即终止,因为当前进程的镜像已经被部分或全部销毁,而且通常没有后续的代码来检查 execl 的返回值或处理错误。

需要注意的是,由于 execl 会替换当前进程的镜像,因此它通常用于创建新进程后立即执行新程序的场景(例如,在 fork 调用之后)。


运行结果

父进程创建子进程,子进程PID=12345
子进程启动,执行命令:ls -l
总用量 8
-rwxr-xr-x 1 user group 8760 May 10 10:00 demo
-rw-r--r-- 1 user group  678 May 10 10:00 demo.c

线程1: 5的平方是25
线程2: 5的立方是125
父进程结束
  1. 子进程调用 ls -l 列出当前目录内容。
  2. 父进程的两个线程分别计算平方和立方。
  3. 所有线程和子进程结束后,父进程退出。

关键点总结

  • fork():创建子进程,父子进程并行执行不同代码。
  • exec 族函数:子进程通过 execl 加载外部命令,替换原有程序。
  • pthread:父进程通过多线程并行处理任务,共享进程资源(如变量 num)。
  • 同步机制waitpidpthread_join 确保进程和线程按顺序结束。

通过这个案例,你可以清晰看到进程、线程和外部命令执行的协作模式。
进程之间的代码执行顺序并不是固定的。

参考逻辑

父进程通过 fork 创建 子进程
子进程调用 execl 执行外部命令(如 ls -l)。
父进程创建 两个线程,分别计算数值的平方和立方。
父进程等待子进程和所有线程结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值