怎么建投票网站,建设网站的发布与推广,桐梓网站开发,网站开发图书管理系统Linux系统 进程 进程私有地址空间用户模式和内核模式上下文切换 进程控制系统调用错误处理进程控制函数获取进程 ID创建和终止进程回收子进程让进程休眠加载并运行程序 进程
异常是允许操作系统内核提供进程#xff08;process#xff09;概念的基本构造块#xff0c;进程是… Linux系统 进程 进程私有地址空间用户模式和内核模式上下文切换 进程控制系统调用错误处理进程控制函数获取进程 ID创建和终止进程回收子进程让进程休眠加载并运行程序 进程
异常是允许操作系统内核提供进程process概念的基本构造块进程是计算机科学中最深刻、最成功的概念之一。异常控制流与进程有密切的关系因为它为进程的多任务运行、异常处理和资源管理提供了基础。
进程是操作系统对正在运行的程序的一种抽象。它为每个程序提供了一种假象仿佛每个程序都在独占地使用处理器、主存和 I/O 设备而实际上系统中可能同时存在多个进程并发执行操作系统负责管理和协调这些进程对硬件资源的共享。
私有地址空间 进程的私有地址空间包括
代码区存放程序代码通常是只读的以防止程序意外修改自身指令。数据区包含已初始化的全局和静态变量。堆区从低地址向高地址增长用于动态内存分配如程序运行过程中根据需要创建的数据结构。栈区从高地址向低地址增长用于存放函数调用相关信息如局部变量、函数参数、返回地址等。
这种布局有助于组织和管理进程的数据和指令存储并且通过地址空间的隔离保证了进程间的独立性和安全性防止一个进程意外访问或修改另一个进程的数据。
用户模式和内核模式 在用户模式下进程只能访问自己的虚拟内存空间无法直接操作硬件或执行特权指令。而在内核模式下进程具有完全的权限可以访问整个内存空间并与硬件直接交互操作系统的核心功能大多在内核模式下执行。
进程从用户模式切换到内核模式通常由系统调用、中断或异常触发。当进程请求操作系统服务时它会通过系统调用触发模式切换。系统调用通过陷阱指令使得进程从用户模式转入内核模式操作系统处理完请求后再返回用户模式继续执行。
上下文切换
进程上下文包含了进程执行所需的所有信息这些信息完整地描述了进程在某一时刻的执行状态当进程被暂停或切换时操作系统需要保存这些上下文信息以便在下次恢复执行时能够准确地回到之前的执行状态。
程序计数器PC的值指示下一条要执行的指令地址。寄存器的值包括通用寄存器、栈指针、程序状态字寄存器用于存储进程当前的计算状态和临时数据。页表用于将虚拟地址转换为物理地址实现内存管理。内核栈用于保存进程在内核态执行时的函数调用信息和临时数据。
进程控制
系统调用错误处理
void unix_error(char *msg)
{fprintf(stderr, %s: %s\n, msg, strerror(errno));exit(0);
}pit_t Fork()
{pit_t pid;if ((pid fork()) 0)unix_error(Fork error);return pid;
}Unix 系统级函数出错时通常返回 -1 并设置 errno上面是对系统调用的错误处理封装下面的部分中都使用错误处理包装函数。它们能够保持代码示例简洁而又不会给你错误的假象认为允许忽略错误检査。包装函数定义在一个叫做 csapp.c 的文件中它们的原型定义在一个叫做 csapp.h 的头文件中可以从 CSAPP 网站上在线地得到这些代码。
进程控制函数
获取进程 ID
#include sys/types.h
#include unistd.hpid_t getpid(void);
pid_t getppid(void);
// 返回调用者或其父进程的 PID。getpid 和 getppid 函数返回一个类型为 pid_t 的整数值在 Linux 系统上它在 types.h 中被定义为 int。
创建和终止进程
#include stdlib.hvoid exit(int status);
// 该函数不返回。exit 函数以 status 退出状态来终止进程或返回一个整数值。
#include sys/types.h
#include unistd.hpid_t fork(void);
// 返回子进程返回 0父进程返回子进程的 PID如果出错则为 -1。父进程调用fork后操作系统会创建一个新的进程控制块PCB和地址空间等资源给子进程然后将父进程的上下文复制到子进程中。之后父进程和子进程从fork函数返回后开始独立执行虽然它们从相同的代码位置继续执行但由于返回值不同可以通过判断返回值来执行不同的代码分支例如父进程可以在fork后继续执行其他任务同时等待子进程完成某些操作而子进程可以执行特定于子进程的任务如执行另一个程序等。
// 使用 fork 创建一个新进程
int main()
{pid_t pid;int x 1;pid Fork();if (pid 0) { /* Child */printf(child : x%d\n, x);exit(0);}/* Parent */printf(parent: x%d\n, --x);exit(0);
}linux ./fork
parentx0
child x2回收子进程
#include sys/types.h
#include sys/wait.hpid_t waitpid(pid_t pid, int *statusp, int options);
// 返回如果成功则为子进程的 PID如果 WNOHANG则为 0如果其他错误则为 -1。pid指定要等待的子进程的 PID进程标识符。
如果 pid 0则等待进程 ID 为 pid 的子进程结束。如果 pid 0则等待与当前进程同属一个进程组的任何子进程结束。如果 pid -1则等待任何子进程结束类似于 wait。如果 pid -1则等待进程组 ID 为 pid 的子进程结束即进程组内的任何子进程。
status指向整型变量的指针用于存储子进程的退出状态。如果该参数为 NULL则不保存子进程的状态。
options控制等待行为的选项常见的选项有
WNOHANG非阻塞模式如果没有子进程退出waitpid 立即返回而不是阻塞。WUNTRACED在子进程停止但没有终止时也返回即使它没有结束。WCONTINUED如果子进程因信号而暂停可以在它继续运行时通知父进程。WNOHANG | WUNTRACED立即返回如果等待集合中的子进程都没有被停止或终止则返回值为 0如果有一个停止或终止则返回值为该子进程的 PID。
子进程的退出状态如果 status 参数不为 NULLwaitpid 会将子进程的退出状态存储在 status 指向的变量中。退出状态可以通过以下宏来解析
WIFEXITED(status)如果子进程正常退出返回真。WEXITSTATUS(status)返回子进程的退出码。只有在 WIFEXITED() 返回为真时才会定义这个状态。WIFSIGNALED(status)如果子进程因为未捕捉到的信号退出返回真。WTERMSIG(status)返回导致子进程终止的信号号码。WIFSTOPPED(status)如果子进程被信号暂停返回真。WSTOPSIG(status)返回导致子进程暂停的信号号码。WIFCONTINUED(status)如果子进程因接收到继续信号如 SIGCONT而恢复返回真。
错误条件如果调用进程没有子进程那么 waitpid 返回 -1并且设置 errno 为 ECHILD。如果 waitpid 函数被一个信号中断那么它返回 -1并设置 errno 为 EINTR。
wait 函数
#include sys/types.h
#include sys/wait.hpid_t wait(int *statusp);
// 返回如果成功则为子进程的 PID如果出错则为 -1。调用 wait(status) 等价于调用 waitpid(-1, status, 0)。
让进程休眠
#include unistd.hunsigned int sleep(unsigned int secs);
// 返回还要休眠的秒数。int pause(void);
// 总是返回 -1。sleep函数将一个进程挂起一段指定的时间。
pause函数让调用函数休眠直到该进程收到一个信号。
加载并运行程序
#include unistd.hint execve(const char *filename, const char *argv[],const char *envp[]);
// 如果成功则不返回如果错误则返回 -1。execve 是一个用于启动新程序的系统调用它是 UNIX 和 Linux 系统中执行程序的重要机制。通过 execve当前进程可以加载并执行一个新的程序文件。执行该系统调用后当前进程的地址空间包括代码、数据、堆栈等将被替换为新程序的内容原进程的代码不再执行。
pathname要执行的程序的路径。它是一个以 null 结尾的字符串指定了要执行的文件的绝对路径或相对路径。argv一个指向参数数组的指针用于传递给新程序的命令行参数。它是一个数组其中第一个元素通常是程序的名称接下来的元素是传递给程序的各个参数。数组的最后一个元素必须是 NULL。envp一个指向环境变量数组的指针。它是一个包含环境变量的数组每个环境变量是一个以 分隔的字符串。数组的最后一个元素必须是 NULL。
int main(int argc, char *argv[], char *envp[]);
argc 是一个整数表示命令行中传递给程序的参数的数量。它的值至少为 1因为 argv[0] 始终是程序的名称或路径。argv 是一个指向字符串数组的指针数组中的每个元素是一个指向以 null 字符结尾的字符串的指针。这些字符串对应于命令行中传递给程序的参数。envp 是一个指向环境变量数组的指针。每个环境变量是一个以 null 字符结尾的字符串它的格式通常是 KEYVALUE例如 PATH/usr/bin。 当 main 开始执行时用户栈的组织结构下图所示。从栈底高地址往栈顶低地址依次看。首先是参数和环境字符串。栈往上紧随其后的是以 null 结尾的指针数组其中每个指针都指向栈中的一个环境变量字符串。全局变量 environ 指向这些指针中的第一个 envp[0] 紧随环境变量数组之后的是以 null 结尾的 argv[] 数组其中每个元素都指向栈中的一个参数字符串。在栈的顶部是系统启动函数 libc_start_main的栈帧。 操作环境数组
#include stdlib.hchar *getenv(const char *name);
// 返回若存在则为指向 name 的指针若无匹配的则为 NULL。getenv 用于从环境变量中获取指定变量的值。环境变量是操作系统用于存储关于当前进程的配置信息的键值对比如用户的主目录、系统路径等。
#include stdlib.hint setenv(const char *name, const char *newvalue, int overwrite);
// 返回若成功则为 0若错误则为 -1。void unsetenv(const char *name);
// 返回无。setenv 用于设置或修改一个环境变量的值。如果该环境变量已存在它将被更新如果不存在它会被创建。
unsetenv 用于删除指定的环境变量。调用该函数后该环境变量在当前进程的环境中将不再可用。