目录
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
技术简单讲解:
在Linux系统中,进程是执行中的程序实例,每个进程都有其独立的地址空间、资源分配和执行流。当一个进程创建了另一个进程时,前者称为父进程,后者称为子进程。这种关系通过进程间的一种特殊层次结构来体现,是操作系统管理并发执行的基础之一。以下是关于Linux下父子进程的关键概念和操作:
创建子进程
在Linux中,通常使用`fork()`系统调用来创建一个新的子进程。`fork()`调用会产生一个与父进程几乎完全相同的副本(除了进程ID和一些与进程相关的资源标识符),然后在父子进程中分别返回不同的值。在父进程中,`fork()`返回的是子进程的PID(进程ID);而在子进程中,`fork()`返回0。
进程ID(PID)
每个进程都有一个唯一的PID,用于在系统中唯一标识该进程。父进程知道子进程的PID,但反过来不一定,子进程并不直接知道其父进程的PID,但可以通过`getppid()`系统调用获取父进程的PID。
资源继承与共享
子进程继承了父进程的大部分资源,包括但不限于打开的文件描述符、信号处理函数、当前工作目录、用户ID和组ID等。但是,某些资源如内存映射区域可以被标记为“写时复制”(Copy-On-Write, COW),这意味着除非子进程或父进程尝试修改这些资源,否则它们共享同一物理内存,以节省资源。
父子进程的独立性
虽然子进程是从父进程创建的,但一旦创建完成,两者就成为相互独立的实体,各自拥有独立的执行路径、堆栈空间和变量副本。父子进程可以通过IPC(进程间通信)机制如管道、信号、共享内存等进行通信和数据交换。
子进程结束与父进程的责任
当子进程执行完毕或因其他原因终止时,它会变成僵尸状态(Zombie),此时它不再占用任何系统资源,仅保留一个很小的内核记录,包含其PID和退出状态。父进程应该通过`wait()`或`waitpid()`系统调用来收集子进程的退出状态,并释放僵尸进程。如果父进程不这样做,子进程会成为孤儿进程,最终由init进程(PID为1)接管。
实例应用
- **后台服务与前台任务分离**:父进程可以启动子进程处理耗时或后台任务,而自身继续执行其他操作。
- **并发处理**:通过创建多个子进程实现简单的并发执行,提高程序的处理能力和响应速度。
- **一次性任务**:创建子进程执行一次性或有风险的操作,即使子进程崩溃也不会影响父进程的稳定性。
理解并合理运用Linux下的父子进程机制,是编写高效、可靠多任务程序的基础。
代码思路:
利用父子进程特性实现文件分割复制
父子进程特性简介
- 独立内存空间:父子进程拥有独立的虚拟地址空间,这意味着它们的数据段、堆、栈等都是分离的。
- 执行顺序:父进程先于子进程执行,子进程是在父进程执行特定系统调用(如
fork()
)后创建的。
文件分割复制策略
要实现文件的分割复制,可以利用上述父子进程特性,通过以下步骤完成:
- 文件预处理:使用文件I/O操作打开源文件和目标文件。
- 开启两个文件,分别用于读取源文件和写入目标文件。
- 计算文件大小:读取源文件,统计其总字节数。
- 可以通过逐字节读取直至遇到文件结束符(EOF)的方式来计算文件大小,或者更高效地使用
lseek()
和fstat()
函数。
- 父进程操作:复制源文件的前半部分至目标文件。
- 计算出文件一半的字节数。
- 使用
read()
和write()
系统调用,循环读取并写入数据,直到完成前半部分的复制。
- 子进程操作:复制源文件的后半部分至目标文件。
- 在父进程中调用
fork()
创建子进程。 - 子进程使用
lseek()
将源文件指针移动到文件中间位置。 - 重复父进程的读写操作,完成剩余数据的复制。
源码:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> int main(int argc, char const *argv[]) { // 文件IO实现cp 父进程复制前一半 子进程复制后一办 // 1. 打开文件 char a[1] = {0}; int src = open(argv[1], O_RDONLY); if (src < 0) { perror("open error"); return -1; } int dest = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); if (dest < 0) { perror("open error"); return -1; } // 2. 计算读取文件的字符个数 int size = lseek(src, 0, SEEK_END) - lseek(src, 0, SEEK_SET); int halfsize = size / 2; ssize_t s; // 3. 开始打印 pid_t pid = fork(); if (pid < 0) { perror("fork error"); return -1; } else if (pid == 0)// 子进程 { lseek(src, l, SEEK_SET); lseek(dest, l, SEEK_SET); for (int i = 0; i < halfsize; i++) { s = read(src, a, 1); write(dest, a, s); } } else// 父进程 { lseek(src, 0, SEEK_SET); lseek(dest, 0, SEEK_SET); for (int i = 0; i < halfsize; i++) { s = read(src, a, 1); write(dest, a, s); } } return 0; }