欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > [C高手编程] 进程编程与IPC

[C高手编程] 进程编程与IPC

2024/10/28 10:18:16 来源:https://blog.csdn.net/suifengme/article/details/141304435  浏览:    关键词:[C高手编程] 进程编程与IPC

在这里插入图片描述

💖💖⚡️⚡️专栏:C高手编程-面试宝典/技术手册/高手进阶⚡️⚡️💖💖
「C高手编程」专栏融合了作者十多年的C语言开发经验,汇集了从基础到进阶的关键知识点,是不可多得的知识宝典。如果你是即将毕业的学生,面临C语言的求职面试,本专栏将帮助你扎实地掌握核心概念,轻松应对笔试与面试;如果你已有两三年的工作经验,专栏中的内容将补充你在实践中可能忽略的新技术和技巧;而对于资深的C语言程序员,这里也将是一本实用的技术备查手册,提供全面的知识回顾与更新。无论处在哪个阶段,「C高手编程」都能助你一臂之力,成为C语言领域的行家里手。

概述

本章深入探讨了C语言中的进程编程和进程间通信(IPC)的概念、创建与管理机制,以及不同类型的IPC机制。通过本章的学习,读者将能够理解进程编程的基本原理,并能在实际编程中正确地运用这些概念。

1. 进程的基本概念

1.1 进程定义

进程是程序在一个数据集合上的运行过程,是系统进行资源分配和调度的基本单位。每个进程都有一个唯一的进程标识符(PID)和一个父进程标识符(PPID),除非它是初始进程。

1.2 进程属性

  • 进程ID (PID):标识进程的唯一编号。
  • 父进程ID (PPID):创建该进程的父进程的ID。
  • 进程状态:运行、就绪、等待、终止等。
  • 进程优先级:影响进程调度的顺序。
  • 进程资源使用情况:CPU时间、内存使用等。
  • 进程工作目录:进程当前的工作目录。
  • 环境变量:进程的环境变量列表。
  • 命令行参数:进程启动时传递给它的命令行参数。

1.3 进程控制

  • 创建:使用fork函数创建子进程。
  • 等待:使用waitwaitpid函数等待子进程结束。
  • 终止:使用exit_exit函数终止进程。

在这里插入图片描述

2. 进程控制

2.1 创建进程

2.1.1 fork函数
  • 函数原型pid_t fork(void);
  • 头文件<unistd.h>
  • 参数:无。
  • 返回值:在父进程中返回子进程的PID,在子进程中返回0。如果调用失败,则返回-1。
  • 示例
#include <unistd.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid < 0) {perror("Fork failed");return 1;} else if (pid == 0) {printf("Child process PID: %d\n", getpid());} else {printf("Parent process PID: %d, Child PID: %d\n", getpid(), pid);}return 0;
}

2.2 终止进程

2.2.1 exit函数
  • 函数原型void exit(int status);
  • 头文件<stdlib.h>
  • 参数:进程退出状态。
  • 示例
#include <stdlib.h>
#include <stdio.h>int main() {printf("Exiting with status 0\n");exit(0);return 0; // This line will never be reached.
}
2.2.2 _exit函数
  • 函数原型void _exit(int status);
  • 头文件<unistd.h>
  • 参数:进程退出状态。
  • 说明_exit函数是直接终止进程而不进行任何清理工作的版本,适用于子进程。
  • 示例
#include <unistd.h>
#include <stdio.h>int main() {printf("Child process exiting\n");_exit(0);return 0; // This line will never be reached.
}

2.3 等待进程

2.3.1 wait函数
  • 函数原型pid_t wait(int *status);
  • 头文件<sys/wait.h>
  • 参数:子进程退出状态。
  • 返回值:子进程的PID,如果没有子进程或出错则返回-1。
  • 示例
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid < 0) {perror("Fork failed");return 1;} else if (pid == 0) { // 子进程printf("Child process exiting\n");_exit(0);} else { // 父进程int status;pid_t wpid = wait(&status);if (wpid == -1) {perror("Wait failed");return 1;}printf("Child process exited with status %d\n", WEXITSTATUS(status));}return 0;
}
2.3.2 waitpid函数
  • 函数原型pid_t waitpid(pid_t pid, int *status, int options);
  • 头文件<sys/wait.h>
  • 参数:特定子进程PID、子进程退出状态、选项。
  • 返回值:子进程的PID,如果没有子进程或出错则返回-1。
  • 示例
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid < 0) {perror("Fork failed");return 1;} else if (pid == 0) { // 子进程printf("Child process exiting\n");_exit(0);} else { // 父进程int status;pid_t wpid = waitpid(pid, &status, 0);if (wpid == -1) {perror("Waitpid failed");return 1;}printf("Child process exited with status %d\n", WEXITSTATUS(status));}return 0;
}

在这里插入图片描述

3. 进程间通信(IPC)

3.1 管道 (Pipes)

3.1.1 简介

管道是一种简单的进程间通信机制,它允许两个进程之间进行单向的数据传输。管道由一对特殊文件描述符组成,一个用于写入数据(写端),另一个用于读取数据(读端)。

3.1.2 函数原型
  • pipe函数
    • 函数原型int pipe(int pipefd[2]);
    • 头文件<unistd.h>
    • 参数:管道描述符数组。
    • 返回值:成功返回0,失败返回-1。
    • 说明:创建一个管道,并返回一个包含两个文件描述符的数组,第一个元素为读端,第二个元素为写端。
3.1.3 示例
#include <unistd.h>
#include <stdio.h>int main() {int pipefd[2];if (pipe(pipefd) == -1) {perror("Pipe creation failed");return 1;}pid_t pid = fork();if (pid < 0) {perror("Fork failed");return 1;} else if (pid == 0) { // 子进程close(pipefd[0]); // 关闭读端char message[] = "Hello, world!";write(pipefd[1], message, strlen(message) + 1);close(pipefd[1]);_exit(0);} else { // 父进程close(pipefd[1]); // 关闭写端char buffer[100];read(pipefd[0], buffer, sizeof(buffer));printf("Received message: %s\n", buffer);close(pipefd[0]);}return 0;
}

3.2 消息队列 (Message Queues)

3.2.1 简介

消息队列是一种更为复杂的IPC机制,允许多个进程通过队列交换数据。消息队列支持多条消息的存储和检索,并且每个消息都可以有自己的类型标签,使得接收者可以根据类型选择性地接收消息。

3.2.2 函数原型
  • msgget函数

    • 函数原型int msgget(key_t key, int flag);
    • 头文件<sys/msg.h>
    • 参数:键值、标志位。
    • 返回值:消息队列标识符,如果出错返回-1。
    • 说明:创建或获取一个消息队列。
  • msgsnd函数

    • 函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • 头文件<sys/msg.h>
    • 参数:消息队列标识符、消息结构体指针、消息长度、标志位。
    • 返回值:成功返回0,失败返回-1。
    • 说明:向消息队列发送一条消息。
  • msgrcv函数

    • 函数原型int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    • 头文件<sys/msg.h>
    • 参数:消息队列标识符、消息结构体指针、最大接收消息长度、消息类型、标志位。
    • 返回值:成功返回接收的消息长度,失败返回-1。
    • 说明:从消息队列接收一条消息。
  • msgctl函数

    • 函数原型int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    • 头文件<sys/msg.h>
    • 参数:消息队列标识符、命令、缓冲区。
    • 返回值:成功返回0,失败返回-1。
    • 说明:执行各种消息队列控制操作,例如删除消息队列。
3.2.3 示例
#include <sys/msg.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>#define MSGKEY 1234typedef struct {long mtype;char mtext[50];
} my_msg;int main() {key_t key = MSGKEY;int msqid = msgget(key, 0666 | IPC_CREAT);if (msqid == -1) {perror("Message queue creation failed");return 1;}my_msg msg;msg.mtype = 1;strcpy(msg.mtext, "Hello, world!");if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) {perror("Message send failed");return 1;}if (msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0) == -1) {perror("Message receive failed");return 1;}printf("Received message: %s\n", msg.mtext);if (msgctl(msqid, IPC_RMID, NULL) == -1) {perror("Message queue removal failed");return 1;}return 0;
}

3.3 共享内存 (Shared Memory)

3.3.1 简介

共享内存是一种高效的IPC机制,它允许多个进程共享同一块内存区域。这种方式比管道和消息队列更高效,因为它不需要复制数据,而是直接访问同一段物理内存。

3.3.2 函数原型
  • shmget函数

    • 函数原型int shmget(key_t key, size_t size, int shmflg);
    • 头文件<sys/shm.h>
    • 参数:键值、共享内存大小、标志位。
    • 返回值:共享内存标识符,如果出错返回-1。
    • 说明:创建或获取一个共享内存段。
  • shmat函数

    • 函数原型void *shmat(int shmid, const void *shmaddr, int shmflg);
    • 头文件<sys/shm.h>
    • 参数:共享内存标识符、共享内存地址、标志位。
    • 返回值:成功返回指向共享内存的指针,失败返回(void *)-1
    • 说明:将共享内存段映射到进程的地址空间。
  • shmdt函数

    • 函数原型int shmdt(const void *shmaddr);
    • 头文件<sys/shm.h>
    • 参数:指向共享内存的指针。
    • 返回值:成功返回0,失败返回-1。
    • 说明:解除共享内存段的映射。
  • shmctl函数

    • 函数原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    • 头文件<sys/shm.h>
    • 参数:共享内存标识符、命令、缓冲区。
    • 返回值:成功返回0,失败返回-1。
    • 说明:执行各种共享内存控制操作,例如删除共享内存段。
3.3.3 示例
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>#define SHMKEY 1234int main() {key_t key = SHMKEY;int shmid = shmget(key, 100, 0666 | IPC_CREAT);if (shmid == -1) {perror("Shared memory creation failed");return 1;}char *shm = shmat(shmid, NULL, 0);if (shm == (char *)-1) {perror("Shared memory attach failed");return 1;}strcpy(shm, "Hello, world!");printf("Data written to shared memory: %s\n", shm);if (shmdt(shm) == -1) {perror("Shared memory detach failed");return 1;}if (shmctl(shmid, IPC_RMID, NULL) == -1) {perror("Shared memory removal failed");return 1;}return 0;
}

3.4 信号量 (Semaphores)

3.4.1 简介

信号量是一种同步原语,用于解决进程间的互斥和同步问题。它可以用来保护共享资源免受并发访问的影响,也可以用来实现进程间的同步。

3.4.2 函数原型
  • semget函数

    • 函数原型int semget(key_t key, int nsems, int semflg);
    • 头文件<sys/sem.h>
    • 参数:键值、信号量集中的信号量数、标志位。
    • 返回值:信号量集标识符,如果出错返回-1。
    • 说明:创建或获取一个信号量集。
  • semop函数

    • 函数原型int semop(int semid, struct sembuf *sops, size_t nsops);
    • 头文件<sys/sem.h>
    • 参数:信号量集标识符、信号量操作数组、操作数。
    • 返回值:成功返回0,失败返回-1。
    • 说明:对信号量集执行操作。
  • semctl函数

    • 函数原型int semctl(int semid, int semnum, int cmd, ...);
    • 头文件<sys/sem.h>
    • 参数:信号量集标识符、信号量索引、命令、额外参数。
    • 返回值:成功返回0或指定值,失败返回-1。
    • 说明:执行各种信号量控制操作,例如设置信号量值。
3.4.3 示例
#include <sys/sem.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>#define SEMKEY 1234union semun {int val;struct semid_ds *buf;unsigned short *array;
};int main() {key_t key = SEMKEY;int semid = semget(key, 1, 0666 | IPC_CREAT);if (semid == -1) {perror("Semaphore creation failed");return 1;}union semun semval;semval.val = 1;if (semctl(semid, 0, SETVAL, semval) == -1) {perror("Semaphore value set failed");return 1;}struct sembuf op;op.sem_num = 0;op.sem_op = -1;op.sem_flg = 0;if (semop(semid, &op, 1) == -1) {perror("Semaphore operation failed");return 1;}op.sem_op = 1;if (semop(semid, &op, 1) == -1) {perror("Semaphore operation failed");return 1;}if (semctl(semid, 0, IPC_RMID, semval) == -1) {perror("Semaphore removal failed");return 1;}return 0;
}

3.5 不同IPC机制的比较

3.5.1 性能
  • 管道:性能较低,因为涉及数据复制。
  • 消息队列:性能中等,比管道稍快,但仍然涉及数据复制。
  • 共享内存:性能最高,因为数据直接在内存中共享,无需复制。
  • 信号量:主要用于同步,性能较高,但不是用于数据传输。
3.5.2 复杂性
  • 管道:相对简单,适用于简单的数据传输。
  • 消息队列:较复杂,支持类型化的消息,适用于复杂的数据交换。
  • 共享内存:最复杂,需要手动管理内存,但提供了高性能的数据交换。
  • 信号量:简单,主要用于同步目的。
3.5.3 可移植性
  • 管道:广泛支持,几乎所有的POSIX系统都支持。
  • 消息队列:POSIX标准的一部分,但并非所有系统都支持。
  • 共享内存:POSIX标准的一部分,但并非所有系统都支持。
  • 信号量:POSIX标准的一部分,但并非所有系统都支持。

3.6 IPC机制的选择

选择合适的IPC机制取决于以下几个因素:

  • 数据传输的需求:如果只需要简单的数据传输,管道可能是最好的选择。
  • 数据的复杂性:如果需要类型化的消息,消息队列是更好的选择。
  • 性能需求:如果对性能有高要求,应考虑使用共享内存。
  • 同步需求:如果主要关注同步,信号量是一个好的选择。
  • 可移植性:如果需要高度可移植的解决方案,管道是最佳选择。

在这里插入图片描述

4. 进程调度

4.1 进程优先级

4.1.1 nice函数
  • 函数原型int nice(int inc);
  • 头文件<unistd.h>
  • 参数:优先级增量。
  • 返回值:当前优先级,失败返回-1。
  • 示例
#include <unistd.h>
#include <stdio.h>int main() {int oldPriority = nice(1);if (oldPriority == -1) {perror("nice failed");return 1;}printf("New priority: %d\n", oldPriority);return 0;
}

4.2 进程调度策略

4.2.1 sched_setscheduler函数
  • 函数原型int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
  • 头文件<sched.h>
  • 参数:进程ID、调度策略、调度参数。
  • 返回值:成功返回0,失败返回-1。
  • 示例
#include <sched.h>
#include <stdio.h>
#include <unistd.h>int main() {struct sched_param param;param.sched_priority = 0; // 默认优先级if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {perror("Setting scheduler failed");return 1;}printf("Scheduler set to SCHED_FIFO\n");return 0;
}

在这里插入图片描述

5. 进程信号

5.1 信号处理

5.1.1 signal函数
  • 函数原型void signal(int signum, void (*handler)(int));
  • 头文件<signal.h>
  • 参数:信号号、信号处理器。
  • 返回值:无。
  • 示例
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void sigHandler(int sig) {printf("Signal received: %d\n", sig);
}int main() {signal(SIGINT, sigHandler);printf("Press Ctrl+C to send SIGINT\n");while (1) {sleep(1);}return 0;
}

5.2 阻塞信号

5.2.1 sigprocmask函数
  • 函数原型int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 头文件<signal.h>
  • 参数:设置方式、新信号掩码、旧信号掩码。
  • 返回值:成功返回0,失败返回-1。
  • 示例
#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main() {sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGINT);if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {perror("Blocking signals failed");return 1;}printf("SIGINT is now blocked\n");sleep(5); // 尝试发送SIGINT,不会被处理return 0;
}

在这里插入图片描述

6. 进程资源限制

6.1 设置资源限制

6.1.1 setrlimit函数
  • 函数原型int setrlimit(int resource, const struct rlimit *rlim);
  • 头文件<sys/resource.h>
  • 参数:资源类型、资源限制。
  • 返回值:成功返回0,失败返回-1。
  • 示例
#include <sys/resource.h>
#include <stdio.h>int main() {struct rlimit rlim;rlim.rlim_cur = 1024; // 当前限制rlim.rlim_max = 1024; // 最大限制if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {perror("Setting resource limit failed");return 1;}printf("Resource limit for RLIMIT_NOFILE set\n");return 0;
}

6.2 获取资源限制

6.2.1 getrlimit函数
  • 函数原型int getrlimit(int resource, struct rlimit *rlim);
  • 头文件<sys/resource.h>
  • 参数:资源类型、资源限制。
  • 返回值:成功返回0,失败返回-1。
  • 示例
#include <sys/resource.h>
#include <stdio.h>int main() {struct rlimit rlim;if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {perror("Getting resource limit failed");return 1;}printf("Current limit for RLIMIT_NOFILE: %ld\n", rlim.rlim_cur);printf("Maximum limit for RLIMIT_NOFILE: %ld\n", rlim.rlim_max);return 0;
}

结论

本章深入探讨了C语言中的进程编程和进程间通信(IPC)的概念、创建与管理机制,以及不同类型的IPC机制。

进程基本概念

  • 进程定义:进程是程序在一个数据集合上的运行过程,是系统进行资源分配和调度的基本单位。
  • 进程属性:包括进程ID (PID)、父进程ID (PPID)、进程状态、进程优先级等。
  • 进程控制:包括创建(fork)、终止(exit, _exit)和等待(wait, waitpid)进程。

进程控制

  • 创建进程:使用fork函数创建子进程。
  • 终止进程:使用exit_exit函数终止进程。
  • 等待进程:使用waitwaitpid函数等待子进程结束。

进程间通信(IPC)

管道 (Pipes)
  • 简介:管道允许两个进程之间进行单向的数据传输。
  • 函数原型int pipe(int pipefd[2]);
  • 头文件<unistd.h>
  • 参数:管道描述符数组。
  • 返回值:成功返回0,失败返回-1。
消息队列 (Message Queues)
  • 简介:消息队列允许多个进程通过队列交换数据。
  • 函数原型
    • int msgget(key_t key, int flag);
    • int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    • int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • 头文件<sys/msg.h>
共享内存 (Shared Memory)
  • 简介:共享内存允许多个进程共享同一块内存区域。
  • 函数原型
    • int shmget(key_t key, size_t size, int shmflg);
    • void *shmat(int shmid, const void *shmaddr, int shmflg);
    • int shmdt(const void *shmaddr);
    • int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 头文件<sys/shm.h>
信号量 (Semaphores)
  • 简介:信号量用于解决进程间的互斥和同步问题。
  • 函数原型
    • int semget(key_t key, int nsems, int semflg);
    • int semop(int semid, struct sembuf *sops, size_t nsops);
    • int semctl(int semid, int semnum, int cmd, ...);
  • 头文件<sys/sem.h>
IPC机制比较
  • 性能:共享内存性能最高,其次是消息队列,然后是管道。
  • 复杂性:共享内存最复杂,其次是消息队列,管道相对简单。
  • 可移植性:管道最广泛支持,其他机制可能受限于特定系统。
IPC机制选择
  • 数据传输需求:管道适合简单的数据传输。
  • 数据复杂性:消息队列适合类型化的消息交换。
  • 性能需求:共享内存适合高性能的数据交换。
  • 同步需求:信号量适合进程间的同步。
  • 可移植性:管道是最可移植的选择。

进程调度

  • 进程优先级:使用nice函数修改进程的优先级。
  • 进程调度策略:使用sched_setscheduler函数设置进程的调度策略。

进程信号

  • 信号处理:使用signal函数注册信号处理器。
  • 阻塞信号:使用sigprocmask函数阻塞信号。

进程资源限制

  • 设置资源限制:使用setrlimit函数设置资源限制。
  • 获取资源限制:使用getrlimit函数获取资源限制。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com