应用——Linux进程编程实例分析

Linux进程编程实例分析

一、基础进程创建与父子关系

1. 基本进程创建(fork.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{

    pid_t ret = fork();
    if(ret>0)
    {
        while(1)
        {
            printf("发视频...\n");
            sleep(1);
        }
    }
    else if (0 == ret)
    {

        while(1)
        {
            printf("接收控制....\n");
            sleep(1);
        }
    }
    else  // <0 -1
    {
        perror("fork");
        return 1;
    }
    return 0;
}

要点

  • 典型的父子进程并发执行模式

  • 通过fork()返回值区分父子进程

  • 创建后父子进程独立运行

2. 父子进程变量共享(fork_var.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int a = 20;
int main()
{

    pid_t ret = fork();
    if(ret>0)
    {
        sleep(3);
        printf("father  a  %d\n",a);
    }
    else if (0 == ret)
    {
        
        a+=10;
        printf("child a is %d\n",a);
    }
    else  // <0 -1
    {
        perror("fork");
        return 1;
    }
    printf("a is %d\n",a);
    return 0;
}

写时复制机制

  • fork()后父子共享内存空间

  • 子进程修改变量时,内核为其分配独立内存副本

  • 父进程的变量值保持不变

3. 获取进程ID(getpid.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    int i=10;
    pid_t ret = fork();
    if(ret>0)
    {
        while(i--)
        {
            printf("发视频... pid:%d ppid:%d\n",getpid(),getppid());
            sleep(1);
        }
    }
    else if (0 == ret)
    {

        while(i--)
        {
            printf("接收控制....pid:%d ppid:%d\n",getpid(),getppid());
            sleep(1);
        }
    }
    else  // <0 -1
    {
        perror("fork");
        return 1;
    }
    printf("pid:%d ppid:%d\n",getpid(),getppid());
    return 0;
}
复制代码
// 获取并打印进程PID和父进程PPID
getpid();  // 当前进程ID
getppid(); // 父进程ID

二、进程终止与退出状态

1. exit() vs _exit()(exit.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    
    printf("hello");
    _exit(10);//exit(10);
    printf("看见了,exit 就没退出\n");
    return 0;
}
复制代码
// 
printf("hello");  // 输出"hello"
exit(10);         // 刷新缓冲区,执行清理函数

// 
printf("hello");  // 无输出(缓冲区未刷新)
_exit(10);        // 直接退出,不刷新缓冲区

关键区别

函数 缓冲区处理 清理函数 标准库调用链
exit() 刷新 执行atexit注册的函数 exit→清理→_exit
_exit() 不刷新 不执行 直接系统调用

2. exit状态码范围

  • 正常范围:-128 ~ 127(8位)

  • 使用WEXITSTATUS(status)获取

三、特殊进程状态

1. 僵尸进程(zombie.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
    pid_t pid = fork();
    if(pid>0)
    {
        while(1)
        {
            sleep(1);
        }
    }
    else if(0 == pid)
    {
        printf("child is pid:%d\n",getpid());
        exit(0);
    }
    else 
    {
        perror("fork");
        return 1;
    }
    return 0;
}
复制代码
// 子进程:立即退出,成为僵尸进程
// 父进程:无限循环,不回收子进程状态

僵尸进程特征

  • 子进程已终止,但父进程未调用wait()

  • PCB未被释放,占用内核资源

  • 可通过ps aux | grep defunct查看

2. 孤儿进程(organ.c)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
    pid_t pid = fork();
    if(pid>0)
    {
        printf("father is pid:%d ppid:%d\n",getpid(),getppid());
        exit(0);
    }
    else if(0 == pid)
    {
        int i =3;
        while(i--)
        {
            printf("child is pid:%d ppid:%d\n",getpid(),getppid());
            sleep(1);
        }
    }
    else 
    {
        perror("fork");
        return 1;
    }
    return 0;
}
复制代码
// 父进程:立即退出
// 子进程:循环3次,每次sleep(1)
// 现象:子进程的ppid变为1(被init进程收养)

孤儿进程处理

  • 父进程退出后,子进程被init进程接管

  • 新父进程(init)负责资源回收

  • 不会成为僵尸进程

四、进程状态回收

1. 基本回收(wait.c)

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{

    pid_t pid = fork();
    if(pid>0)
    {
        
        printf("father pid:%d\n",getpid());
        pid_t recycle = wait(NULL);
        printf("recycle pid %d\n",recycle);
        while(1)
        {
            sleep(1);
        }

    }
    else if(0 == pid)
    {
        printf("child  pid:%d\n",getpid());
        sleep(3);
        exit(1);
    }

    else 
    {
        perror("fork");
        return 1;
    }
    return 0;
}
复制代码
// 父进程:阻塞等待任意子进程退出
wait(NULL);  // 不关心退出状态
// 回收成功后继续执行

2. 带状态回收(wait2.c)

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{

    pid_t pid = fork();
    if(pid>0)
    {
        
        printf("father pid:%d\n",getpid());
        int status=0;// 退出状态值  32bit 
        pid_t recycle = wait(&status);
        if(WIFEXITED(status)) //正常结束
        {
            
            printf("正常结束,recycle pid %d,子进程的退出值:%d\n",recycle,WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status)) //异常结束
        {
            
            printf("异常结束,recycle pid %d,信号数:%d\n",recycle, WTERMSIG(status));
        }


    }
    else if(0 == pid)
    {
        printf("child  pid:%d\n",getpid());
        sleep(10);
        exit(50); // -128-127   退出值  8bit
    }

    else 
    {
        perror("fork");
        return 1;
    }
    return 0;
}
复制代码
int status;
wait(&status);  // 获取退出状态

// 状态检查宏:
WIFEXITED(status)    // 是否正常退出
WEXITSTATUS(status)  // 获取退出值(正常退出时)
WIFSIGNALED(status)  // 是否信号终止
WTERMSIG(status)     // 获取信号编号(信号终止时)

状态值存储结构

  • status是32位整数

  • 高16位:退出值/信号编号

  • 低16位:退出原因标志位

五、多进程创建与控制

1. 创建多个子进程(many_child.c)

cpp 复制代码
#include <stdio.h>
#include <unistd.h>

int main()
{
    int n =5;
    int i = 0 ;
    printf("father pid:%d ppid:%d\n",getpid(),getppid());
    for(i = 0 ;i<5;i++)
    {
        pid_t pid = fork();
        if(pid>0)
        {
            continue;
        }
        else if (0 == pid)
        {
        
            printf("child pid:%d ppid:%d\n",getpid(),getppid());
            return 0; // break;
        }
        else 
        {
            perror("");
            return 1;
        }
    }
    return 0;
}
复制代码
// 父进程循环创建5个子进程
// 关键:子进程创建后立即return,避免继续fork

六、进程数量监控(log_proc_num.c)

1. 功能说明

  • 父子进程分别统计系统中进程数量

  • 将统计结果和时间戳写入日志文件

  • 父子进程执行频率不同(父3秒,子5秒)

2. 进程统计方法

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>

int isnum(char* name)
{
    while(*name)
    {
        if(*name>='0'&& *name<='9')
        {

        }
        else
        {
            return 0;
        }
        name++;
    }
    return 1;

}

int proc_count(char *pathname)
{
    DIR *dir = opendir(pathname);
    if (NULL == dir)
    {
        perror("proc_count opendir\n");
        return 1;
    }
    int count = 0;
    while (1)
    {
        struct dirent *info = readdir(dir);
        if (NULL == info)
        {
            break;
        }
        if(DT_DIR == info->d_type)
        {
            if(isnum(info->d_name))
            {
                count++;
            }
        }
    }


    closedir(dir);
    return count;
}


int main(int argc, char *argv[])
{

    FILE* fp = fopen("log","w");
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    pid_t pid=fork();
    if(pid>0)
    {
    
        int i  = 0;
        for(i=0;i<3;i++)
        {
            int num = proc_count("/proc");
            time_t tm;
            time(&tm);
            struct tm *info = localtime(&tm);
            fprintf(fp,"father, pid: %d proc_num:%d %d:%d:%d\n",getpid(),num,info->tm_hour,info->tm_min,info->tm_sec);
            fflush(fp);
            sleep(3);
        }
    }
    else if (0 == pid)
    {
        int i  = 0;
        for(i=0;i<2;i++)
        {
            int num = proc_count("/proc");
            time_t tm;
            time(&tm);
            struct tm *info = localtime(&tm);
            fprintf(fp,"child, pid: %d proc_num:%d %d:%d:%d\n",getpid(),num,info->tm_hour,info->tm_min,info->tm_sec);
            fflush(fp);
            sleep(5);
        }
    }
    else 
    {
        perror("fork");
        return 1;
    }

    return 0;
}
复制代码
// 统计/proc目录下的数字目录名
// 每个数字目录对应一个进程
int proc_count(char *pathname) {
    // 遍历/proc目录
    // 检查目录名是否全为数字
    // 统计有效进程目录数量
}

进程标识

  • /proc/[pid]/ - 进程信息目录

  • 有效进程目录名均为数字

七、进程编程总结

1. 进程创建流程

  1. 调用fork()创建子进程

  2. 根据返回值区分父子进程

  3. 父子进程各自执行代码

  4. 子进程结束前向父进程传递退出状态

  5. 父进程调用wait()回收子进程

2. 常见问题与解决

问题 原因 解决方案
僵尸进程 父进程未调用wait() 父进程wait()回收
孤儿进程 父进程先退出 由init进程自动回收
缓冲区不刷新 使用_exit()退出 使用exit()或fflush()
多进程循环fork 子进程继续执行循环 子进程中break/return

3. 最佳实践

  1. 资源回收:父进程应及时调用wait()回收子进程

  2. 状态检查:使用WIFEXITED等宏检查退出原因

  3. 进程同步:考虑进程间通信和同步需求

  4. 错误处理:检查fork()返回值,处理创建失败情况

  5. 日志记录:关键操作添加日志,便于调试

4. 系统调用对比

函数 类型 主要用途
fork() 系统调用 创建新进程
exit() 库函数 正常退出,清理资源
_exit() 系统调用 立即退出,不清理
wait() 系统调用 阻塞等待子进程结束
getpid() 系统调用 获取当前进程ID
getppid() 系统调用 获取父进程ID
相关推荐
prettyxian4 小时前
【linux】从 0 到 1 理解程序启动:冯诺依曼体系、操作系统与系统调用的协同密码
linux·运维·服务器
罗不丢4 小时前
docker镜像配置
运维·docker·容器
电子_咸鱼4 小时前
【QT——信号和槽(1)】
linux·c语言·开发语言·数据库·c++·git·qt
DuHz4 小时前
《Around the Corner mmWave Imaging in Practical Environments》论文精读
论文阅读·算法·信息与通信·毫米波雷达
牛奶咖啡134 小时前
Linux系统故障排查思路实践教程(上)
linux·服务器·linux系统故障排查思路·linux的日志分类与分析·忘记linux用户密码问题解决·系统无法启动问题解决·linux文件系统只读问题解决
杰克逊的日记4 小时前
k8s某pod节点资源使用率过高,如何调整
linux·docker·kubernetes
大道之简4 小时前
SpringAI入门学习
学习
Lueeee.4 小时前
Linux内核模块的编译
linux
CoderYanger4 小时前
D.二分查找-基础-2529. 正整数和负整数的最大计数
java·开发语言·数据结构·算法·leetcode·职场和发展