Linux自学day24-进程和线程2

news/2025/2/22 15:19:35

一    进程结束

这段代码的主要功能是创建一个子进程,子进程休眠 10 秒后以退出状态码 10 结束,父进程等待子进程结束并回收其资源,同时根据子进程的退出状态输出相应的信息。

int main(int argc, const char **argv)
{
    // 定义一个 pid_t 类型的变量 pid,用于存储 fork 函数的返回值
    pid_t pid;
    // 定义一个 pid_t 类型的变量 ret,用于存储 wait 函数的返回值
    pid_t ret;
    // 定义一个整型变量 status,用于存储子进程的退出状态
    int status;

    // 调用 fork 函数创建一个新的子进程
    // fork 函数会返回两次:在父进程中返回子进程的 PID,在子进程中返回 0,出错时返回 -1
    pid = fork();
    // 检查 fork 函数是否调用失败
    if (-1 == pid)
    {
        // 如果失败,调用自定义的错误信息输出宏 ERR_MSG 输出错误信息
        ERR_MSG("fail to fork");
        // 程序返回 -1 表示出错
        return -1;
    }

    // 判断当前是子进程还是父进程
    // 如果 pid 为 0,说明当前是子进程
    if (0 == pid)
    {
        // 输出子进程开始执行的信息,同时输出子进程的 PID
        printf("子进程(PID:%d)开始执行\n", getpid());
        // 子进程暂停执行 10 秒
        sleep(10);
        // 子进程以退出状态码 10 结束
        exit(10);
    }
    // 如果 pid 大于 0,说明当前是父进程
    else if (pid > 0)
    {
        // 输出父进程开始执行的信息,同时输出父进程的 PID
        printf("父进程(PID:%d)开始执行\n", getpid());
        // 父进程调用 wait 函数等待子进程结束
        // wait 函数会阻塞父进程,直到有一个子进程结束,并将子进程的退出状态存储在 status 中
        // wait 函数返回结束的子进程的 PID
        ret = wait(&status);
        // 输出父进程回收子进程空间的信息,同时输出回收的子进程的 PID
        printf("回收到子进程(PID:%d)空间了\n", ret);
        // 检查子进程是否正常结束
        if (WIFEXITED(status))
        {
            // 如果正常结束,使用 WEXITSTATUS 宏获取子进程的退出状态码并输出
            printf("正常结束,值:%d\n", WEXITSTATUS(status));
        }
        // 检查子进程是否是因为收到信号而异常结束
        else if (WIFSIGNALED(status))
        {
            // 如果是被信号杀死,使用 WTERMSIG 宏获取杀死子进程的信号编号并输出
            printf("被 %d 号信号杀死\n", WTERMSIG(status));
        }
    }

    // 主函数正常返回 0
    return 0;
}

二      exec函数族

hello.c

该文件定义了一个简单的 C 程序,其主要功能是打印一条问候信息、当前进程的进程 ID(PID),并遍历命令行参数列表,将每个参数的索引和值打印出来。

#include <stdio.h>
#include <unistd.h>

// main函数是程序的入口点
// argc 是命令行参数的数量,argv 是一个指向命令行参数字符串数组的指针
int main(int argc, char *argv[])
{
    int i = 0;  // 定义一个整型变量 i 并初始化为 0

    // 打印 "hello world!" 以及当前进程的进程 ID
    printf("hello world! PID:%d\n", getpid());

    // 遍历命令行参数数组
    for (i = 0; i < argc; i++)
    {
        // 打印每个命令行参数的索引和其值
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    return 0;  // 程序正常结束,返回 0
}
main.c

该文件定义了另一个 C 程序,其主要功能是使用 execv 系统调用执行 hello.c 编译生成的可执行文件 ./hello,并传递一组命令行参数。如果 execv 调用失败,会输出错误信息。

#include <stdio.h>
#include "public.h"

// main函数是程序的入口点
// argc 是命令行参数的数量,argv 是一个指向命令行参数字符串数组的指针
int main(int argc, const char **argv)
{
    // 定义一个字符指针数组 parg,用于存储要传递给 execv 函数的参数
    char *parg[5] = {
        "./hello",  // 要执行的程序的路径
        "world",    // 第一个参数
        "thank",    // 第二个参数
        "you",      // 第三个参数
        NULL,       // 参数列表的结束标志
    };

    // 打印 "exec上面!" 以及当前进程的进程 ID
    printf("exec上面! PID:%d\n", getpid());

    // 使用 execv 函数执行 "./hello" 程序,并传递 parg 数组中的参数
    // 如果 execv 函数执行成功,下面的代码将不会被执行
    // 如果 execv 函数执行失败,将继续执行下面的代码
    execv("./hello", parg);

    // 打印错误信息,表示 execv 函数执行失败
    ERR_MSG("fail to execl");

    // 打印 "看到我就表示execl失败了"
    printf("看到我就表示execl失败了\n");

    return 0;  // 程序正常结束,返回 0
}

三     system的用法

这段代码的主要功能是创建一个子进程,并在子进程中执行 ls -l 命令,父进程等待子进程结束后继续执行并打印提示信息。同时,代码中包含了一段被注释掉的用于打印环境变量的代码。

#include <stdio.h>
// 包含自定义的公共头文件,通常用于声明一些通用的宏、函数或结构体等
#include "public.h"
// 引入外部变量 environ,这是一个指向环境变量字符串数组的指针,
// 数组中的每个元素都是一个以 "变量名=值" 形式表示的环境变量
extern char **environ;

// 主函数,程序的入口点
// argc 表示命令行参数的数量,argv 是一个指向命令行参数数组的指针
int main(int argc, const char **argv)
{
    // 定义一个整型变量 i,用于循环计数
    int i = 0;
    // 定义一个 pid_t 类型的变量 pid,用于存储 fork 函数返回的进程 ID
    pid_t pid;

    // 打印提示信息,表明程序执行到此处
    printf("system上面!\n");

    // #if 0 和 #endif 之间的代码块被注释掉,不会被编译执行
    // 此代码块的作用是打印当前进程的所有环境变量
#if 0
    // 打印分隔线,用于区分输出内容
    printf("=========================================\n");
    // 循环遍历 environ 数组,直到遇到 NULL 元素
    for (i = 0; environ[i] != NULL; i++)
    {
        // 打印每个环境变量
        printf("%s\n", environ[i]);
    }
    // 打印分隔线,用于区分输出内容
    printf("=========================================\n");
#endif

    // 注释掉的 system 函数调用,system 函数用于执行一个 shell 命令
    // system 函数会创建一个子进程来执行指定的命令
    // system("ls -l");

    // 调用 fork 函数创建一个新的子进程
    // fork 函数会返回两次,在父进程中返回子进程的进程 ID,在子进程中返回 0,出错时返回 -1
    pid = fork();
    // 检查 fork 函数是否调用失败
    if (-1 == pid)
    {
        // 调用自定义的错误处理函数 ERR_MSG 打印错误信息
        // 该函数通常会输出错误信息到标准错误输出
        ERR_MSG("fail to fork");
        // 程序出错,返回 -1 表示异常退出
        return -1;
    }
    // 判断是否为子进程
    if (0 == pid)
    {
        // 子进程中调用 execlp 函数执行 ls -l 命令
        // execlp 函数会用新的程序替换当前进程的映像
        // 第一个参数 "ls" 是要执行的程序名,后续参数是传递给该程序的命令行参数,最后一个参数必须为 NULL
        execlp("ls", "ls", "-l", NULL);
    }
    // 判断是否为父进程
    else if (pid > 0)
    {
        // 父进程中调用 wait 函数等待子进程结束
        // wait 函数会阻塞父进程,直到任意一个子进程结束
        // 这里传递 NULL 表示不关心子进程的退出状态
        wait(NULL);
        // 子进程结束后,父进程打印提示信息
        printf("system下面!\n");
    }

    // 程序正常结束,返回 0 表示成功
    return 0;
}

 四     pthread线程使用

#include <stdio.h>
// 包含自定义的公共头文件,可能包含一些自定义的宏、函数声明或结构体定义等
#include "public.h"

// 定义线程函数,该函数将在新线程中执行
// 参数 arg 是传递给线程函数的参数,这里未使用
// 返回值为 void* 类型,是线程退出时返回的状态信息
void *thread_fun(void *arg)
{
    // 打印线程开始执行的信息,使用 pthread_self() 函数获取当前线程的线程ID
    // %#x 是格式化输出十六进制数,并带有 0x 前缀
    printf("线程(TID:%#x)开始执行\n", (unsigned int)pthread_self());

    // 线程函数执行完毕,返回 NULL 表示正常退出
    return NULL;
}

// 主函数,程序的入口点
// argc 是命令行参数的数量,argv 是存储命令行参数的字符串数组
int main(int argc, const char **argv)
{
    // 定义一个 pthread_t 类型的变量 tid,用于存储新创建线程的线程ID
    pthread_t tid;
    // 定义一个整型变量 ret,用于存储函数调用的返回值,初始化为 0
    int ret = 0;

    // 调用 pthread_create 函数创建一个新线程
    // &tid 是用于存储新线程ID的变量的地址
    // NULL 表示使用默认的线程属性
    // thread_fun 是新线程要执行的函数
    // NULL 表示不传递任何参数给新线程的函数
    ret = pthread_create(&tid, NULL, thread_fun, NULL);
    // 检查 pthread_create 函数的返回值
    // 如果返回值不为 0,表示线程创建失败
    if (ret != 0)
    {
        // 调用 ERR_MSG 宏输出错误信息,提示线程创建失败
        ERR_MSG("fail to pthread_create");
        // 线程创建失败,返回 -1 表示程序异常退出
        return -1;
    }

    // 进入一个无限循环,使主线程一直运行
    // 这里的无限循环会导致主线程一直占用 CPU 资源,直到程序被手动终止
    while (1)
    {
        // 循环体为空,不执行任何操作
    }

    // 程序正常退出,返回 0 表示程序成功执行
    return 0;
}

四     pthread线程创建

#include <stdio.h>
// 包含自定义的公共头文件,通常用于包含一些公共的函数声明、宏定义等
#include "public.h"

// 线程函数,该函数将作为线程的执行体
// 参数 arg 是传递给线程的参数,类型为 void*,可以传递任意类型的数据
void *thread(void *arg)
{
    // 将传递进来的参数 arg 转换为 int 类型,并赋值给 num 变量
    int num = (int)arg;

    // 打印当前线程的编号和线程 ID(以十六进制格式输出)
    printf("线程%d(TID:%#lx)正在执行\n", num, pthread_self());

    // 线程执行完毕,返回 NULL
    return NULL;
}

// 主函数,程序的入口点
int main(int argc, const char **argv)
{
    // 定义一个包含 3 个 pthread_t 类型元素的数组,用于存储创建的线程 ID
    pthread_t tid[3];
    // 循环计数器
    int i = 0;
    // 定义一个包含 3 个整数的数组,用于存储传递给线程的参数
    int a[3] = {1, 2, 3};
    // 注释掉的代码,原本用于定义一个函数指针数组,指向不同的线程函数
    // void *(*pfun[3])(void *) = {thread1, thread2, thread3};

    // 循环创建 3 个线程
    for (i = 0; i < 3; i++)
    {
        // 创建线程,将线程 ID 存储在 tid[i] 中
        // NULL 表示使用默认的线程属性
        // thread 是线程的执行体函数
        // (void *)(i+1) 是传递给线程的参数,将 i+1 转换为 void* 类型
        pthread_create(&tid[i], NULL, thread, (void *)(i+1));
    }

    // 循环等待 3 个线程执行完毕
    for (i = 0; i < 3; i++)
    {
        // 等待 tid[i] 对应的线程执行完毕
        // NULL 表示不获取线程的返回值
        pthread_join(tid[i], NULL);
    }
    // 主函数返回 0,表示程序正常结束
    return 0;
}


http://www.niftyadmin.cn/n/5862442.html

相关文章

飞书API

extend目录下,API <?php // ---------------------------------------------------------------------- // | 飞书API // ---------------------------------------------------------------------- // | COPYRIGHT (C) 2021 http://www.jeoshi.com All rights reserved. …

电商API安全防护:JWT令牌与XSS防御实战

在电商 API 的安全防护中&#xff0c;JWT&#xff08;JSON Web Token&#xff09;令牌用于身份验证和授权&#xff0c;而 XSS&#xff08;跨站脚本攻击&#xff09;防御则是防止恶意脚本注入&#xff0c;保护用户数据和系统安全。以下是这两方面的实战介绍。 JWT 令牌实战 1. 生…

flowable适配达梦数据库

文章目录 适配相关问题无法从数据库产品名称“DM DBMS”中推断数据库类型分析解决 构建ibatis SqlSessionFactory时出错&#xff1a;inStream参数为null分析解决 liquibase相关问题问题一&#xff1a;不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访…

网络安全-Mysql注入知识点

本篇文章介绍sql注入时数据库是Mysql时需要掌握的知识点。以Navicat、mysql 5.7.4为例。 注释 #或--空格是单行注释 /**/是内联注释 SQL语句 查询语句 SELECT〈目标列组〉 FROM〈数据源〉 [WHERE〈元组选择条件〉] [GROUP BY〈分列组〉[HAVING 〈组选择条件…

MySql面试宝典【刷题系列】

文章目录 一、Mysql 的存储引擎 myisam 和 innodb 的区别。二、MySQL数据库作发布系统的存储&#xff0c;一天五万条以上的增量&#xff0c;预计运维三年,怎么优化&#xff1f;三、对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题&#xff1f;四、锁的优化策略…

代码随想录算法训练day59---图论系列4

代码随想录算法训练 —day59 文章目录 代码随想录算法训练前言一、110.字符串接龙二、105.有向图的完全可达性dfs版本1dfs版本2bfs版本 三、100. 岛屿的最大面积方法一方法二 总结 前言 今天是算法营的第59天&#xff0c;希望自己能够坚持下来&#xff01; 今天继续图论part&…

工控自动化领域:数字量信号与模拟量信号的差异解析

在工控自动化的神秘世界里&#xff0c;信号如同传递指令和信息的使者&#xff0c;而数字量信号和模拟量信号则是其中的两大主角。它们各自有着独特的 “性格” 和 “使命”&#xff0c;在不同的场景中发挥着关键作用。下面&#xff0c;就让我们一起来深入了解一下它们的区别。 …

x安全服务 y安全体系 z网络安全模型 网络安全体系设计

这一年来&#xff0c;网络安全行业兴奋异常。各种会议、攻防大赛、黑客秀&#xff0c;马不停蹄。随着物联网大潮的到来&#xff0c;在这个到处都是安全漏洞的世界&#xff0c;似乎黑客才是安全行业的主宰。然而&#xff0c;我们看到的永远都是自己的世界&#xff0c;正如医生看…