一、进程
进程是操作系统进行资源分配和调度运行的基本单位。
进程的创建:通过 fork 函数能够创建子进程。在实际应用中,不仅可以创建单个子进程,还能创建多个子进程。创建完成后,通过特定的标识或条件,可以清晰地区分父子进程的代码逻辑,从而实现不同的功能和任务分配。
例如,在一个多任务处理的系统中,父进程可以通过 fork 函数创建多个子进程来分别处理不同的计算任务,提高整体的处理效率。
进程的执行:子进程在执行过程中,存在两种常见的情况。
执行与父进程相同的事情:这可能是对相同任务的并行处理,以加快完成速度。
执行与父进程不同的事情:比如父进程负责数据收集,子进程负责数据分析。
进程的退出:进程的退出方式有多种,如 return、exit、_exit 等。这些方式在不同的场景下使用,以满足程序的特定需求。
进程的特殊状态:
孤儿进程:当父进程先于子进程结束时,子进程就成为了孤儿进程。不过,孤儿进程并不会带来危害,因为操作系统会自动为其重新分配父进程,通常是 init 进程,所以一般不需要特别处理。
僵尸态进程:当子进程结束后,其资源尚未被完全回收,处于僵尸态。为了避免资源浪费和系统异常,需要通过特定的方法进行处理。
例如,如果多个子进程频繁处于僵尸态而未被处理,可能会导致系统资源逐渐耗尽,影响整个系统的性能。
在处理子进程的状态改变和资源回收方面,有两个重要的函数:wait 和 waitpid 。
wait(&status) 函数是一种阻塞操作,它会使调用者一直处于等待状态,直到子进程的状态发生改变。在获取子进程的退出状态时,只有最低 8 位是有效的,范围在 0 到 255 之间。通过 WIFEXITED 函数先判断是否为正常退出,再使用 WEXITSTATUS 函数获取 exit 传递的退出状态值。
waitpid(pid_t pid, int *wstatus, int options) 函数则更加灵活,它可以指定等待特定的子进程状态改变。
pid = -1 表示等待所有子进程。
pid > 0 表示等待指定的子进程。
同时,还可以通过设置 options 参数实现非阻塞调用。
options = WNOHANG 表示非阻塞。
options = 0 表示默认的阻塞。
在非阻塞调用时,父进程会主动查看子进程的状态改变情况,如果没有发生改变,父进程不会被阻塞,程序会继续向下执行。由于非阻塞调用可能无法立即获取到子进程的状态改变,因此通常需要将其嵌套在循环中进行轮询处理。
二、线程
线程被视为轻量级的进程。
线程存在的必要性:与传统的进程相比,线程在创建和调度时所产生的时空开销显著减小。这使得线程成为了 CPU 执行的最小单位,而进程则侧重于资源的分配。
例如,在一个网络服务器中,处理多个并发连接时,使用线程可以更高效地利用系统资源,减少上下文切换的开销。
线程的组成部分:线程由线程 tid(标识符)、程序计数器、寄存器集合以及栈等组成。这些组件共同协作,确保线程能够独立执行并保存其执行状态。
线程与进程的关系:
线程存在于进程内部,一个进程可以包含多个线程。
线程共享进程的资源,包括代码段、数据段、打开的文件以及接收到的信号等。这种共享机制在一定程度上提高了资源的利用率,但也需要注意线程之间的同步和互斥问题,以避免数据竞争和不一致性。
线程的结束并不一定会导致所属进程的结束,只有当进程内的所有线程都结束时,进程才会真正终止。
以一个图形处理软件为例,一个进程中可能有多个线程,分别负责图像的读取、处理和显示,即使某个处理线程结束,只要其他线程仍在运行,进程就会继续存在。
线程的编程实现:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) 函数用于创建线程。
thread:线程标识符,需要事先定义并由该函数返回。
attr:线程属性,一般为 NULL 表示默认属性。
start_routine:线程执行函数(线程回调函数),是一个函数指针。
arg:回调函数的参数。
主函数所在的执行流被称为主线程,其他由 pthread_create 创建的线程则为子线程。在一个多线程程序中,各个线程的地位是平等的,它们按照操作系统的调度策略并发执行。
线程的退出可以通过 pthread_exit(void *retval) 函数实现。需要注意的是,如果在 main 函数中调用 pthread_exit ,则表示结束主线程。但此时,主线程的结束并不意味着整个进程的结束,只有当进程内的其他线程也都结束后,进程才会终止。
retval:退出状态值,传的是退出状态值对应的地址。
对于线程的资源回收,使用 pthread_join(pthread_t thread, void **retval) 函数。
thread:线程 tid 。
retval:用于保存退出状态值所在空间的地址。