Linux进程控制(附代码解析)
1.认识fork
1.1什么是fork
在Linux中,fork是一个系统调用(syscall),用于创建一个新的进程。它通过复制当前进程(称为父进程)的内存、状态和文件描述符等信息来创建一个完全相同但独立运行的子进程。fork系统调用具有以下特点和用途:
-
创建子进程:fork系统调用被用于创建一个新的子进程,使得父进程和子进程在之后的执行过程中可以独立运行。
-
复制父进程:fork系统调用会将父进程的所有内容(包括内存、变量、文件描述符等)复制到子进程中,使得子进程与父进程初始状态相同。
-
进程分叉:fork系统调用的结果是父进程分叉出一个子进程,子进程成为父进程的一个副本。父进程和子进程会从fork调用之后的下一条指令开始并行执行,但各自独立运行。
-
进程间通信:通过fork创建的父子进程可以通过进程间通信(如管道、共享内存、消息队列等)来实现数据的交换和共享。
fork系统调用的返回值有以下几种情况:
- 返回值为负数:表示fork调用失败,无法创建子进程。
- 返回值为0:表示当前进程是子进程,可以通过获取父进程的ID(getppid())得到父进程的进程ID。
- 返回值大于0:表示当前进程是父进程,返回值为子进程的进程ID。
通过fork系统调用,可以实现并发执行、多任务处理和进程间通信,是实现多进程编程的重要基础。
1.2fork和vfork有什么区别
vfork和fork是在Linux中用于创建子进程的两个系统调用,它们之间有一些重要的区别。
-
内存空间的共享方式不同:在fork中,父进程的地址空间会被完全复制给子进程,子进程拥有独立的地址空间副本。而在vfork中,子进程会直接共享父进程的地址空间,子进程在执行期间不会创建父进程的副本。这意味着在vfork中,子进程对共享内存的修改会直接影响到父进程。
-
执行顺序的不同:在fork中,父进程和子进程之间的执行顺序是不确定的,取决于操作系统的调度。而在vfork中,子进程会先于父进程执行,直到子进程调用exec或者exit函数之后,父进程才会继续执行。
-
文件描述符的处理不同:在fork中,父进程的文件描述符会被复制到子进程,子进程会继承父进程打开的文件。而在vfork中,子进程会共享父进程的文件描述符表,因此在vfork中需要谨慎操作文件描述符,避免产生不可预料的结果。
总结来说,fork创建的子进程是父进程的副本,有独立的地址空间和文件描述符表,执行顺序不确定。而vfork创建的子进程直接共享父进程的地址空间,执行顺序是先子进程后父进程,对共享内存和文件描述符的修改会直接影响到父进程。通常情况下,使用fork更为常见,而vfork则用于特定的场景,如在创建子进程后立即调用exec来执行新的程序,以节省资源和提高效率。
2.写时拷贝机制
2.1是什么是写时拷贝
-
写时拷贝(Copy-on-Write,COW)是一种操作系统中的内存管理技术,它用于在创建子进程时延迟进行内存拷贝,以提高效率和减少内存消耗。
-
当一个进程(称为父进程)调用fork()创建子进程时,操作系统并不立即为子进程分配新的内存空间来存储它的数据。相反,子进程将与父进程共享相同的物理内存页。
-
在写时拷贝的机制下,当父进程或子进程试图修改共享的内存页时,才会触发实际的内存拷贝操作。这意味着在修改之前,父进程和子进程将共享相同的内存页。这样可以节省内存,因为在初始阶段并没有进行实际的内存拷贝。
当父进程或子进程中的任意一个进程尝试修改共享的内存页时,操作系统会执行以下操作:
- 操作系统会为修改操作所在的内存页创建一个副本。
- 修改操作仅对副本进行,而不会影响其他进程的内存页。
- 父进程和子进程分别引用各自的内存页,实现了数据的分离。
通过这种方式,每个进程都有了自己的独立内存副本,从而避免了数据互相干扰。
写时拷贝的好处在于,当父进程创建子进程时,不需要立即进行大规模的内存拷贝操作,从而提高了效率和速度。只有在需要修改共享内存页时,才会进行实际的内存拷贝操作。
需要注意的是,写时拷贝只适用于对内存页的写操作。对于只读操作,父进程和子进程仍然共享相同的内存页。只有在写操作发生时,才会进行拷贝。
总结起来,写时拷贝是一种延迟内存拷贝的技术,通过共享父进程和子进程的内存页来节省内存和提高效率。只有在进行写操作时,才会执行实际的内存拷贝操作,使得父进程和子进程拥有各自的独立内存副本
2.2写时拷贝机制优点
-
节省内存消耗:使用写时拷贝,父进程和子进程可以共享相同的内存页,而无需立即进行内存拷贝。这样可以节省内存空间,特别是在创建子进程时,避免了大规模的内存拷贝操作。
-
提高创建进程的效率:在创建子进程时,写时拷贝机制避免了对整个父进程内存空间的复制,只需复制需要修改的部分。这样可以提高创建进程的效率,减少了时间开销。
-
避免数据冗余:写时拷贝确保父进程和子进程在初始阶段共享相同的内存页,只有在需要修改时才会进行拷贝。这避免了不必要的数据冗余,提高了系统的整体性能。
-
数据隔离和安全性:写时拷贝确保了父进程和子进程在进行写操作时拥有各自的独立内存副本。这样可以避免数据互相干扰,提高了数据的隔离性和安全性。
-
增加并发性:通过写时拷贝,父进程和子进程可以并发地执行操作,而无需相互等待或加锁。只有在需要修改共享的内存页时,才会引发实际的内存拷贝操作。这样提高了并发性和系统的响应能力。
总而言之,写时拷贝机制在创建进程、节省内存、提高效率、保证数据隔离和安全性等方面都具有重要的作用。它是一种有效的内存管理技术,被广泛应用于操作系统和编程语言的实现中。
3.进程终止
3.1什么是进程终止
-
进程终止是指进程的执行结束或被终止的状态。当一个进程达到终止状态时,它不再执行任何指令,并释放占用的系统资源。进程终止可以是正常的,也可以是异常的。
-
进程正常终止通常是指进程按照预期完成了它的任务,并主动调用了退出操作。这可以是进程执行完所有指令、达到程序结尾的位置,或者在执行过程中遇到了退出指令(如exit())或终止信号(如SIGTERM)而退出。正常终止时,进程会经历一系列的清理操作,包括释放内存、关闭文件和释放其他资源。
-
进程异常终止指的是进程在执行过程中遇到了错误、异常或不可恢复的情况,导致进程被迫终止。这可能是由于访问非法内存、除零错误、资源不足、操作系统错误等原因引起的。异常终止时,操作系统通常会记录错误信息、生成核心转储文件(core dump)以供调试,并进行一些必要的清理工作。
-
进程终止后,其占用的系统资源将被操作系统回收,包括内存、文件描述符、进程标识符等。同时,操作系统还会通知进程的父进程,以便父进程能够获取子进程的终止状态和执行相关的后续处理。
了解进程终止状态对于进程管理和错误处理非常重要。通过监控进程的终止状态,可以及时发现问题并采取相应的措施,如重新启动进程、清理资源、记录日志等。
3.2 Main函数中的return 0
-
main函数中,进程结束之后返回“0”,这个0代表状态码,表示进程成功运行结束。
-
进程的退出状态码是一个整数值,用于向操作系统和其他程序传达进程的退出状态信息。在C/C++中,返回值0通常表示成功或正常终止,而非零值表示错误或异常终止。这种约定是广泛接受的编程实践,但具体的返回值含义可能因操作系统或程序的约定而有所不同。
-
常见的异常终止码或错误码是非零值,用于指示不同的错误类型。例如,Unix/Linux操作系统定义了一些标准的退出状态码,如1表示一般错误,2表示命令行参数错误等。不同的操作系统和程序可能有自己定义的错误码,用于表示特定的错误情况。
3.3常见进程终止状态
- 代码跑完 ---->结果符合预期
- 代码跑完----->结果不符合预期
- 代码异常退出
正对上述三种情况,我们一般只会研究后两者,第一种正常达到预期,我们不去研究。针对达不到预期或者异常的程序我们要研究原因,这就要查看程序的状态码。
3.4 如何查看结束状态码
- echo $?
linux下设置的状态码: