Linux进程与终端学习笔记
进程与程序的区别
-
程序:二进制文件,存储在磁盘上
-
进程:process,一个程序运行实例
- 将程序从磁盘加载到内存并分配对应的资源、调度运行
-
进程实例
- 汇编指令代码、数据、资源、状态
- 一个虚拟计算机(进程上下文环境、CPU状态寄存器)
- 进程资源:虚拟内存、打开的文件描述符表、信号、工作目录…
-
进程由操作系统来调度管理.
-
程序是静态的
-
进程是动态的
创建一个进程:fork
系统调用:fork()
- 函数原型:pid_t fork(void);
- 函数作用:创建一个新进程
- 返回值:
- -1 :创建子进程失败
- 0 :在子进程中返回0
- >0 :在父进程中返回的是子进程的PID
子进程的运行
子进程拷贝父进程
- 代码、数据、堆栈内存
- 进程资源:打开的文件描述符、信号、缓冲区…
执行一个二进制程序文件
execvp函数
**函数原型:int execvp (const char file, char const argv[]);
- 功能说明:将当前进程的代码使用file程序文件代替并执行
- 参数说明
- file:要执行的程序名称
- argv:要执行的程序文件的参数列表,参数列表以NULL指针为结束标记
- 返回值
- 成功:无返回值
- 失败:返回-1,并设置errno值
exec函数簇
- #include <unistd.h>
- int execl (const char *path, const char *arg, …);
- int execlp (const char *file, const char *arg, …);
- int execle (const char *path, const char *arg, …);
- int execv (const char *path, char *const argv[]);
- int execvp (const char *file, char *const argv[]);
- int execvpe (const char *file, char *const argv[], char *const envp[]);
exec函数簇命名规则
- L:参数以列表的形式提供
- V:参数以数组(向量)的方式提供
- E:为新进程提供新的环境变量
- P:在用户的绝对路径path下查找可执行文件,该文件必须在用户路径下,可以只指定程序文件名
写时复制(COW)与vfork
一个新进程的诞生:虚拟空间
一个新进程的诞生:物理空间
一个新进程的诞生
写时复制(copy-on-write)
对fork-exec流程的改进
- 对于代码段、数据段等,父子进程可以共享,节省拷贝开销
- 父子进程的页表项均指向同一块物理内存页帧
- 当子进程进程空间的内容要修改时,才会真正将段复制到子进程
- 写时复制:
- 仅仅为子进程复制父进程的虚拟页表项
- 对将要修改的页面修改页表项
系统调用:vfork
- 对fork的改进
- 对fork的改进更为彻底、简单粗暴
- vfork是为子进程立即执行exec的程序而专门设计的
- 无需为子进程复制虚拟内存页或页表,子进程直接共享父进程的资源,直到其成功执行exec或是调用exit退出
- 在子进程调用exec之前,将暂停执行父进程
进程的退出
终止当前进程exit函数
- POSIX标准和ANSI C定义的标准函数
- #include <stdlib.h>
- 其实是对系统调用_exit的封装
- 函数原型:void exit (int status);
- 函数功能:终止当前进程
- 参数说明:用于标识进程的退出状态,shell或父进程可以获取该值
- 0:表示进程正常退出
- -1/1:表示进程退出异常
- 2~n:用户可自定义
exit函数背后
执行流程
- 调用退出处理程序(通过atexit、on_exit注册的函数)
- 刷新stdio流缓冲区
- 使用由status提供的值执行_exit系统调用函数
- 关闭进程打开的文件描述符、释放进程持有的文件锁
- 关闭进程打开的信号量、消息队列
- 取消该进程通过mmap创建的内存映射
- …
atexit/on_exit
- 退出处理程序
- 在exit退出后可以自动执行用户注册的退出处理程序
- 执行顺序与注册顺序相反
- 函数原型:int atexit (void (*function)(void));
- 函数原型:int on_exit (void (*function)(int , void *), void *arg);
TIPS
return与exit的区别
- exit用来终止当前进程,将控制权交给操作系统
- return用来退出当前函数,销毁栈帧,返回到上级函数执行
- 终止进程:
- 正常退出:exit、_exit、从main函数return
- 异常退出:调用abort、信号ctrl + C
exit_group函数
- 函数原型: void exit_group (int status);
- exit:退出当前进程process
- exit_group:退出一个进程中所有threads
- Linux系统特有的系统调用,不属于POSIX标准
other
- fork之后、exec之前,使用exit是不安全的
- 很多资源还是共享的(如文件描述符、缓冲区)
exit与_exit
两者的区别
- exit是库函数是对_exit系统调用的封装
- 在调用_exit之前,它会执行各种动作
- 调用退出处理程序(通过atexit和on_exit注册的回调函数)
- 刷新stdio流缓冲区
- 使用由status提供的值执行_exit系统调用
_exit的执行流程
- 关闭进程打开的文件描述符、释放该进程持有的文件锁
- 关闭该进程打开的信号量、消息队列
- 取消该进程通过mmap()创建的内存映射
- 将该进程的所有子进程交给init托管
- 给父进程发送一个SIGCHLD信号
- ……
_exit和exit总结
- 在一个进程中,直接调用_exit终止进程,缓冲区的数据可能会丢失
- 在创建子进程的应用中,只应有一个进程(一般为父进程)调用exit终止,而其他进程应调用_exit()终止。从而确保只有一个进程调用退出处理程序并刷新stdio缓冲区
- 如果一个进程使用atexit/on_exit注册了退出管理程序,则应使用exit终止程序的运行,否则注册的回调函数无法执行