嵌入式Linux应用编程(二):多进程编程
本文最后更新于 33 天前,其中的信息可能已经有所发展或是发生改变。

进程的基本概念

进程是程序的执行实例,是动态的、运行中的实体。程序是静态的概念,进程是动态的概念。每个进程都有一个唯一的非负整数标识符,称为PID(Process ID)。getpid()获取自身进程ID,getppid()获取父进程ID


fork()——创建子进程

函数原型

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

pid_t fork(void);

返回值

fork()创建一个新进程(子进程),子进程几乎是父进程的复制版本,获得父进程的栈、数据段、堆和执行文本段的拷贝

  • 父进程中返回子进程的PID
  • 子进程中返回0
  • 失败返回-1

代码案例:fork使用

/**
 * fork_demo.c - 演示fork()创建子进程
 * 编译:gcc fork_demo.c -o fork_demo
 * 运行:./fork_demo
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int global_var = 100;  // 全局变量

int main() {
    pid_t pid;
    int local_var = 50;  // 局部变量
    
    printf("Before fork: PID=%d, global=%d, local=%d\n", getpid(), global_var, local_var);
    
    pid = fork();
    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程
        printf("\n[Child] PID=%d, PPID=%d\n", getpid(), getppid());
        printf("[Child] Before change: global=%d, local=%d\n", global_var, local_var);
        global_var = 200;
        local_var = 100;
        printf("[Child] After change: global=%d, local=%d\n", global_var, local_var);
        exit(0);
    } else {
        // 父进程
        wait(NULL);  // 等待子进程结束
        printf("\n[Parent] PID=%d, Child PID=%d\n", getpid(), pid);
        printf("[Parent] After child terminated: global=%d, local=%d\n", 
               global_var, local_var);
        printf("[Parent] Note: changes in child do NOT affect parent\n");
    }
    
    return 0;
}

运行结果如下:

Before fork: PID=1234, global=100, local=50

[Child] PID=1235, PPID=1234
[Child] Before change: global=100, local=50
[Child] After change: global=200, local=100

[Parent] PID=1234, Child PID=1235
[Parent] After child terminated: global=100, local=50
[Parent] Note: changes in child do NOT affect parent
  1. 父子进程是两个完全独立的进程
  2. 子进程会拷贝父进程的所有数据(全局变量、局部变量)
  3. 子进程修改变量,父进程完全不受影响
  4. 它们有不同的 PID

exec函数族——执行新程序

在当前进程里,替换成另一个程序来运行。

  • 进程 PID 不变
  • 原来的代码、数据全部被清空替换
  • 运行新的程序
  • 执行成功不返回,直接跑新程序

使用exec函数的原因:Linux 所有命令(ls、ps、ifconfig、./a.out)都是由父进程fork()后,再exec()运行的!

函数原型

#include <unistd.h>

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[]);

/*
l = list:参数一个一个写
v = vector:参数用数组
p = path:自动去系统 PATH 找命令
e = environment:带环境变量
*/

execlp("ls","ls","-l",NULL);
char *agrv[] = {"ls","-l",NULL};
execvp(argv[0],argv);

进程调用exec函数族执行某个程序时,进程当前内容被指定的程序替换,从而实现让父子进程执行不同的程序

代码案例:fork+exec组合

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

int main(int argc, char const *argv[])
{
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        // 子进程:执行Ping命令
        printf("正在执行子进程:%d\n", getpid());

        // 使用execvp(v:数组,p:寻找PATH)
        char *args[] = {"ping", "-c", "10", "www.baidu.com", NULL};
        execvp(args[0], args);

        // 如果执行成功,以下代码不会执行
        perror("exec");
        exit(EXIT_FAILURE);
    }
    else
    {
        wait(NULL);
        printf("子进程%d执行完毕,父进程%d退出\n", pid, getpid());
    }
    return 0;
}

exit()、_exit()和wait()/waitpid()

函数原型

#include <stdlib.h>
void exit(int status);

#include <unistd.h>
void _exit(int status);

#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

功能说明

  • exit():标准C库函数,终止进程时会刷新所有stdio缓冲区
  • _exit():系统调用,立即终止进程,不刷新缓冲区
  • wait():阻塞等待任意子进程终止,回收子进程资源,防止产生僵尸进程
  • waitpid():可指定等待的特定子进程,提供更精细的控制

守护进程

守护进程(Daemon)是在后台运行的进程,通常脱离终端控制,独立于用户登录会话。

  • 后台运行
  • 脱离终端控制(关闭终端不退出)
  • 父进程是 1 号进程(init/systemd)
  • 日志输出到文件 /syslog

创建守护进程的方法(代码):

/**
 * daemon_demo.c - 创建守护进程并定期写日志
 * 编译:gcc daemon_demo.c -o daemon_demo
 * 运行:./daemon_demo
 * 查看:tail -f /tmp/daemon.log
 * 停止:killall daemon_demo
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <string.h>

#define LOG_FILE "/tmp/daemon.log"

void write_log(const char *msg) {
    FILE *fp;
    time_t now;
    struct tm *tm_info;
    char timestamp[64];
    
    fp = fopen(LOG_FILE, "a");
    if (fp == NULL) return;
    
    time(&now);
    tm_info = localtime(&now);
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
    
    fprintf(fp, "[%s] %s\n", timestamp, msg);
    fclose(fp);
}

// 信号处理函数
void signal_handler(int sig) {
    if (sig == SIGTERM) {
        write_log("Daemon received SIGTERM, shutting down");
        exit(0);
    }
}

int main() {
    pid_t pid;
    
    // 第1步:fork,使父进程退出
    pid = fork();
    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        // 父进程退出
        exit(0);
    }
    
    // 第2步:setsid()创建新会话,成为会话组长
    if (setsid() < 0) {
        perror("setsid failed");
        exit(EXIT_FAILURE);
    }
    
    // 第3步:忽略SIGHUP信号
    signal(SIGHUP, SIG_IGN);
    
    // 第4步:二次fork(可选,确保不获取终端)
    pid = fork();
    if (pid < 0) {
        perror("second fork failed");
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        exit(0);
    }
    
    // 第5步:更改工作目录到根目录
    chdir("/");
    
    // 第6步:清空文件权限掩码
    umask(0);
    
    // 第7步:关闭不需要的文件描述符
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    
    // 将标准输入/输出/错误重定向到/dev/null
    open("/dev/null", O_RDWR);  // stdin
    dup(0);  // stdout
    dup(0);  // stderr
    
    // 第8步:设置信号处理
    signal(SIGTERM, signal_handler);
    
    // 记录启动日志
    write_log("Daemon started");
    
    // 守护进程主循环
    int count = 0;
    while (1) {
        sleep(30);  // 每30秒执行一次任务
        count++;
        char msg[128];
        snprintf(msg, sizeof(msg), "Daemon heartbeat #%d", count);
        write_log(msg);
    }
    
    return 0;
}

本篇文章到此结尾了,感谢阅读!

感谢支持!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇