Linux C 进程与进程间通信

本文介绍了 Linux 下的进程管理及进程间通信(IPC),包括 fork 创建子进程、wait 和 waitpid 函数等待子进程结束、exec 函数族执行外部程序、僵尸进程和孤儿进程的概念。同时详细讲解了 Linux 下的 IPC 方式,如共享内存、消息队列、管道、信号等,并提供了丰富的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux C 进程与进程间通信

进程常用API

  • getpid 获取当前进程ID
  • getppid 获取当前进程的父进程ID
  • fork 创建一个进程, 子进程中返回0, 父进程中返回子进程pid.
  • wait函数族 等待子进程结束, 回收子进程资源.
    • wait 等待所有子进程结束
    • waitpid 等待特定子进程结束(也可所有)
  • exec函数族 执行外部程序, 完全替换当前进程内容, 后缀意义如下.
    l: 参数为可变长参数(NULL结尾)
    v: 参数为数组
    p: 在当前环境变量中搜索
    e: 传入环境变量再在其中搜索
    • execl, execlp, execle
    • execv, execvp, execve
  • exit函数族 退出程序
    • exit 标准库函数, 退出前进行缓冲区清理等操作.
    • _exit 直接退出, 不做任何处理.

创建进程

进程结束的顺序是不确定的, 用wait来保证子进程先结束.

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    pid_t chi_pid;
    int ret, chi_stat;

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        printf("child process running, pid: %d, ppid: %d .\n", getpid(), getppid());

        sleep(2);

        printf("child process finished, pid: %d, ppid: %d .\n", getpid(), getppid());
        exit(0);
    } else {
    /* parent process */
        printf("parent process running, pid: %d, ppid: %d .\n", getpid(), getppid());
        ret = wait(&chi_stat);
        if_error(ret, "wait");

        /* wait success */
        if (WIFEXITED(chi_stat)) {
            printf("wait success, status code: %d .\n", WEXITSTATUS(chi_stat));
        }

        /* wait failed */
        if (WIFSIGNALED(chi_stat)) {
            printf("wait failed, status code: %d .\n", WTERMSIG(chi_stat));
        }

        printf("parent process finished, pid: %d, ppid: %d .\n", getpid(), getppid());
    }
    printf("main exit.\n");
    return 0;
}

僵尸进程

僵尸进程: 已经执行结束, 但是资源还未被回收的进程.
利用system函数执行shell脚本, 发现进程状态为Z的僵尸进程.

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 80

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    pid_t chi_pid;
    char shell_cmd[MAXLINE];

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        exit(0);
    } else {
    /* parent process */
        sleep(2);
    }

    sprintf(shell_cmd, "ps aux | grep %d", chi_pid);
    system(shell_cmd);

    printf("main exit.\n");
    return 0;
}

孤儿进程

孤儿进程: 父进程执行结束, 子进程还在运行的进程.(会被init进程接管)
执行结束后查看对应子进程的PPID, 是init进程.

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 80

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    pid_t chi_pid;
    char shell_cmd[MAXLINE];

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        printf("before parent exit, pid: %d, ppid:%d\n", getpid(), getppid());
        sleep(2);
        printf("after parent exit, pid: %d, ppid:%d\n", getpid(), getppid());
        sleep(2);
        exit(0);
    } else {
    /* parent process */
        printf("parent exit, child pid: %d .\n", chi_pid);
        exit(0);
    }
}

两次fork避免僵尸进程

第二次fork产生孙子进程, 让子进程直接结束, 孙子进程变成孤儿进程被init接管.

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    pid_t chi_pid, grand_chi_pid;
    int ret;

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        grand_chi_pid = fork();
        if_error(grand_chi_pid, "fork");

        if (grand_chi_pid == 0) {
        /* grand child process */
            sleep(2);
            printf("grand child process pid: %d, ppid: %d .\n", getpid(), getppid());
            exit(0);
        } else {
        /* child process */
            printf("child process pid: %d, ppid: %d .\n", getpid(), getppid());
            exit(0);
        }
    } else {
    /* parent process */
        ret = waitpid(chi_pid, NULL, 0);
        printf("parent process pid: %d, ppid: %d .\n", getpid(), getppid());
    }
    printf("main exit.\n");
    return 0;
}

exec执行shell命令

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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define MAXLINE 80
#define MAXCNT 20

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

char* cmd_parse(char *buf, char **args)
{
    int cnt = 0;
    char *cmd;

    args[cnt++] = cmd = strtok(buf, " ");

    do {
        args[cnt] = strtok(NULL, " ");
    } while (args[cnt++] != NULL);

    return cmd;
}

int main(int argc, char **argv)
{
    int chi_pid, ret;
    char buf[MAXLINE], cmd_exit[MAXLINE];
    char *cmd, *args[MAXCNT];

    while (1) {
        chi_pid = fork();
        if_error(chi_pid, "fork");

        if (chi_pid == 0) {
        /* child process */
            printf("input cmd: ");
            fgets(buf, MAXLINE, stdin);
            buf[strlen(buf)-1] = '\0';

            /* parse cmd */
            cmd = cmd_parse(buf, args);
            ret = execvp(cmd, args);
            if_error(ret, "execvp");
        } else {
        /* parent process */
            chi_pid = wait(NULL);
            if_error(chi_pid, "wait");
        }
    }
    return 0;
}

进程间通信(IPC)

Linux下的IPC方式有共享内存, 消息队列, 管道, 信号等.
共享内存和消息队列需要root权限才能执行

  • 共享内存
    • shmget 创建/访问 共享内存对象
    • shmat 把共享内存区对象映射到调用进程的地址空间
    • shmdt 断开共享内存连接
    • shmctl 对共享内存提供控制
  • 消息队列
    • msgget 创建/访问 消息队列
    • msgrcv 从消息队列中获取消息
    • msgsnd 把消息添加到消息队列中
    • msgctl 对消息队列提供控制
  • 管道
    • pipe 为文件描述符数组建立无名管道
    • mkfifo 创建命名管道(FIFO)文件
  • 信号
    • signal 设置信号捕捉句柄
    • kill 发送信号

共享内存

利用内存进行通信的方式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#include <sys/ipc.h>
#include <sys/shm.h>

#define IPC_KEY 2048
#define MAXLINE 80
#define MAXSIZE MAXLINE*100

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    pid_t chi_pid;
    int shm_id, ret;
    char *shm_ptr;
    struct shmid_ds shm_stat;

    char buf[MAXLINE];
    char output[MAXSIZE], content[MAXSIZE];

    memset(content, 0, sizeof(content));

    shm_id = shmget(IPC_KEY, MAXSIZE, IPC_CREAT);
    if_error(shm_id, "shmget");

    shm_ptr = shmat(shm_id, NULL, 0);
    if_error(*(int*)shm_ptr, "shmat");

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        printf("input msg:\n");
        while (1) {
            /* input msg */
            fgets(buf, MAXLINE, stdin);
            buf[strlen(buf)-1] = '\0';
            /* finish input msg */
            if (strncmp(buf, "exit", 4) == 0) break;
            strcat(content, buf);
        }
        /* cp content to shm_ptr */
        memcpy(shm_ptr, content, MAXSIZE);
    } else {
    /* parent process */
        chi_pid = wait(NULL);
        if_error(chi_pid, "wait");

        /* get content from shm_ptr */
        memcpy(output, shm_ptr, MAXSIZE);
        printf("%s\n", output);
        /* get shm info */
        shmctl(shm_id, IPC_STAT, &shm_stat);
        printf("PID: %d, shared memory size is %ld\n", getpid(), shm_stat.shm_segsz);
        /* disconnect */
        ret = shmdt(shm_ptr);
        if_error(ret, "shmdt");
    }
    return 0;
}

管道

半双工通信

无名管道

用于子进程和父进程的简单通信方式

#include <unistd.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define MAXLINE 80

void if_error(int stat_code, char* err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    int fd[2], ret;
    pid_t chi_pid;
    int rd_n, wr_n;
    char input[MAXLINE], buf[MAXLINE];
    /*  setup pipe
        fd[0] read pipe
        fd[1] write pipe
    */
    ret = pipe(fd);
    if_error(ret, "pipe");

    chi_pid = fork();
    if_error(chi_pid, "fork");

    if (chi_pid == 0) {
    /* child process */
        close(fd[0]);
        printf("child process send msg:\n");
        while (1) {
            /* send msg to parent process */
            fgets(input, MAXLINE, stdin);
            input[strlen(input)-1] = '\0';

            wr_n = write(fd[1], input, strlen(input));
            if_error(wr_n, "read");

            /* child process exit */
            if (strncmp(input, "exit", 4) == 0) {
                printf("child process exit .\n");
                exit(0);
            }
        }
    } else {
    /* parent process */
        close(fd[1]);
        while (1) {
            /* recv msg from child process */
            rd_n = read(fd[0], buf, MAXLINE);
            if_error(rd_n, "read");
            buf[rd_n] = '\0';

            /* if recv exit msg */
            if (strncmp(buf, "exit", 4) == 0) {
                ret = wait(NULL);
                if_error(ret, "wait");
                printf("parent process exit .\n");
                break;
            }
            printf("parent process recv msg: %s\n", buf);
        }
    }
    return 0;
}
命名管道

用于两个进程间的简单通信
read.c

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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define MAXLINE 80

void if_error(int stat_code, char* err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    int fd, rd_n, ret;
    char *fifo_path = "./fifo";
    char buf[MAXLINE];

    /* setup fifo file */
    ret = mkfifo(fifo_path, 0664);
    if (ret < 0 && errno!=EEXIST) return -1;

    /* open fifo file */
    fd = open(fifo_path, O_RDONLY);
    if_error(fd, "open");

    printf("process %d opening FIFO with read-only .\n", getpid());

    while (1) {
        /* read pipe msg */
        rd_n = read(fd, buf, MAXLINE);
        if_error(rd_n, "read");
        buf[rd_n] = '\0';

        /* recv exit msg */
        if (strncmp(buf, "exit", 4) == 0) break;
        printf("recv pipe msg: %s\n", buf);
    }
    printf("read finished\n");
    /* remove fifo file */
    unlink(fifo_path);
    close(fd);
    return 0;
}

write.c

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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define MAXLINE 80

void if_error(int stat_code, char* err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int main(int argc, char **argv)
{
    int fd, ret, wr_n;
    char *fifo_path = "./fifo";
    char buf[MAXLINE];

    /* setup fifo file */
    ret = mkfifo(fifo_path, 0664);
    if (ret < 0 && errno!=EEXIST) return -1;

    /* open fifo file */
    fd = open(fifo_path, O_WRONLY);
    if_error(fd, "open");

    printf("process %d opening FIFO with write-only .\n", getpid());

    while (1) {
        /* send pipe msg */
        printf("send pipe msg:");
        fgets(buf, MAXLINE, stdin);
        buf[strlen(buf)-1] = '\0';
        wr_n = write(fd, buf, strlen(buf));
        if_error(wr_n, "write");

        /* exit msg */
        if (strncmp(buf, "exit", 4) == 0) break;
    }
    printf("write finished\n");
    /* remove fifo file */
    unlink(fifo_path);
    close(fd);
    return 0;
}

消息队列

与命名管道类似的一种通信机制, 可以发送数据块.
read.c

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define IPC_KEY 1024
#define MAXLINE 80

void if_error(int stat_code, char* err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

struct msg_ds
{
    char data[MAXLINE];
};

int main(int argc, char **argv)
{
    int msq_id, rd_n;
    struct msg_ds msg;

    msq_id = msgget(IPC_KEY, IPC_CREAT);
    if_error(msq_id, "msgget");

    while (1) {
        /* recv msg */
        rd_n = msgrcv(msq_id, (void*)&msg, MAXLINE, 0, 0);
        if_error(rd_n, "msgrcv");
        msg.data[rd_n] = '\0';
        /* exit msg */
        if (strncmp(msg.data, "exit", 4) == 0) break;

        printf("recv msg: %s\n", msg.data);
    }
    msgctl(msq_id, IPC_RMID, 0);
    return 0;
}

write.c

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

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define IPC_KEY 1024
#define MAXLINE 80

void if_error(int stat_code, char* err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

struct msg_ds
{
    char data[MAXLINE];
};

int main(int argc, char **argv)
{
    int msq_id, wr_n;
    struct msg_ds msg;

    msq_id = msgget(IPC_KEY, IPC_CREAT);
    if_error(msq_id, "msgget");

    while (1) {
        /* send msg */
        printf("send msg: ");
        fgets(msg.data, MAXLINE, stdin);
        msg.data[strlen(msg.data)-1] = '\0';

        wr_n = msgsnd(msq_id, &msg, MAXLINE, 0);
        if_error(wr_n, "msgsnd");
        /* exit msg */
        if (strncmp(msg.data, "exit", 4) == 0) break;
    }
    msgctl(msq_id, IPC_RMID, 0);
    return 0;
}

信号

发送信号和捕捉信号的通信方式

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

void if_error(int stat_code, char *err_msg)
{
    if (stat_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int get_sig = 0;

void sig_alarm_handler(int sig)
{
    get_sig = 1;
}

int main(int argc, char **argv)
{
    pid_t chi_pid;
    chi_pid = fork();
    if_error(chi_pid, "fork");
    /* setup signal handler */
    signal(SIGALRM, sig_alarm_handler);

    if (chi_pid == 0) {
    /* child process */
        sleep(5);
        kill(getppid(), SIGALRM);
        exit(0);
    } else {
    /* parent process */
        while (1) {
            sleep(1);
            /* check if recv sig */
            if (get_sig) {
                printf("get signal SIGALRM(%d)\n", SIGALRM);
                break;
            } else {
                printf("not get signal\n");
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值