【1】进程
-
进程是程序的一次执行过程
-
程序和进程之间的区别:
- 程序
程序是保存在磁盘上的可执行的二进制文件
是静态、是指令的集合,没有执行的过程。 - 进程:一个独立的可调度的任务
执行一个程序所分配的资源的总称
进程是程序的一次执行过程
进程是动态的,包括创建、调度(时间片)、执行和消亡
- 程序
-
进程包含三个段:
- “数据段”存放的是全局变量、常数,static修饰的变量等。
- “正文段”存放的是程序中的代码
- “堆栈段”存放的是函数的返回地址、函数的参数以
及程序中的局部变量,malloc开辟的内存空间。
产生一个进程会分配0-4G的虚拟内存空间:
0-3G:用户私有空间
3-4G: 所有进程公用的空间
-
进程内存管理
-
每个进程都分配4G虚拟内存空间
-
0G~3G 是用户空间, 3G~4G 是内核空间
-
3G~4G 是所有进程共享的
(mmu:内存管理单元,虚拟机只有mmu能操作物理地址,管理用户
使用的物理地址其他用户将不能在获取使用)
代码段、数据段、bss段、只读数据段、
堆段、栈段(都占用自己的虚拟地址并映射相应的物理地址) -
进程是程序执行和资源管理的最小单位。
包含的的资源有:物理内存、文件描述符、虚拟地址 0G~4G、
CPU时间片、进程号(PID唯一标识一个进程)
-
-
进程的类型:
- 交互进程:交互进程既可以在前台运行,也可以在后台运行。
该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户
的输入后,该类进程会立刻响应。 - 批处理进程:该类进程不属于某个终端,
它被提交到一个队列中以便顺序执行 - 守护进程:该类进程在后台运行。它一般在Linux启动时
开始执行,系统关闭时才结束。
- 交互进程:交互进程既可以在前台运行,也可以在后台运行。
-
进程的运行状态:
- 运行态:此时进程或者正在运行,或者准备运行。
- 等待态:此时进程在等待一个事件的发生或某种系统资源。
可中断:处在这种状态下的进程可以被信号中断,
接收到信号或被显示地唤醒呼叫,唤醒之后,
进程将转变为运行态。
不可中断:它不会处理信号,只有在它所等待的
事件发生时,进程才被显示的唤醒 - 停止态:此时进程被中止。
- 死亡态:这是一个已终止的进程,但还在进程向量
数组中占有一个task_struct结构。
D uninterruptible sleep (usually IO)(不可中断的等待态)
R running or runnable (on run queue)(运行态)
S interruptible sleep (可中断的等待态)
T stopped, either by a job control signal or because it is
being traced.(停止态)
X dead (should never be seen)(死亡态)
Z defunct (“zombie”) process, terminated but not reaped by its
parent.(僵尸态)For BSD formats and when the stat keyword is used, additional
characters may be displayed:
< high-priority (not nice to other users)(高优先级)
N low-priority (nice to other users)(低优先级)
s is a session leader(会话组组长)
多个进程可以组成一个组,多个组可以组成一个会话。
多个会话可以组成一个会话组。
l is multi-threaded (线程)- is in the foreground process group.(前台)
空:表示后台
-
【2】进程的相关命令
ps:
ps -aux 查看进程信息
ps -ef 父进程pid
ps -ajx 查看进程组id 会话id 父进程pid
top
nice sudo nice -5 ./a.out
NI 值越小,优先级越高 -20~19
PR NI+20
renice
sudo renice -3 pid
kill:
kill -l :查看信号
kill -9 pid
前后进程切换的命令:
fg:将后台进程调到前台
bg:后台暂停的进程在后台运行起来
jobs:查看后台有那些作业(任务)
[1]pid runing ./a.out &
fg %1
【3】创建进程
-
fork
#include <unistd.h>
pid_t fork(void);
功能:创建一个子进程
参数:无
返回值:
失败:-1
成功:父进程中返回的是子进程的pid >0
子进程中返回的是 0 =0-
一旦执行fork函数,进程就变成了两个进程,子进程拷贝父进程的代码段
正文段、堆栈段。子进程几乎拷贝了父进程的所有内容,除去父进pid、task_struct 等。fork创建的子进程和父进程之间是相互独立,都有属于自己的0-4G虚拟内存
空间。一旦fork产生子进程之后,子进程和父进程之间的操作相互独立。 -
fork之前的代码会被复制,但是不会被执行,之后的代码会被复制一份并执行。
-
如果父进先退出,子进程会变成孤儿进程,被init进程收养,并且成为后台进程。
-
如果子进程先退出,父进程没有给子进程回收资源,子进程就会变成僵尸进程,占用内存资源
-
验证:fork之前打开的文件。fork之后父子进程拿到同一个文件描述符,那是否是操作的是同一个
文件指针?(文件只被打开一次,只有一个文件指针) 是
-
补充:写时拷贝
由于之前的fork完整地拷贝了父进程的整个地址空间,
因此执行速度是比较慢的。为了提高效率,Unix系统
设计者创建了现代unix版的fork。现代unix版的fork
也创建新进程,但不产生父进程的副本。它通过允许
父子进程可访问相同物理内存从而伪装了对进程地址
空间的真实拷贝,当子进程需要改变内存中数据时才
拷贝父进程。这就是著名的"写操作时拷贝"
(copy-on-write)技术
【4】退出进程的函数exit/_exit
-
#include <stdlib.h>
void exit(int status);
功能:退出进程
参数:status:退出进程的状态
返回值:无 -
#include <unistd.h>
void _exit(int status);
功能:退出进程
参数:status:退出进程的状态
返回值:无
exit退出进程会刷新缓存区,_exit退出进程不刷新缓存区
【5】获取进程pid和父进程pid的函数
- getpid/getppid
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
返回进程的PID
pid_t getppid(void);
返回父进程的PID
注意:这两个函数是永远不会失败的函数。
【6】回收子进程资源
-
wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:阻塞等待子进程退出回收子进程资源
参数:status:保存子进程的退出状态
返回值:
成功:退出进程的进程号
失败:-1? status若为空,表示忽略子进程退出时的状态
? status若不为空,表示保存子进程退出时的状态
另外,子进程的结束状态可由Linux中一些特定的宏来测定。
返回值:成功:子进程的进程号
失败:-1WIFEXITED(*status)
判断子进程是否正常结束//wifexited
//正常结束返回1,非正常结束返回0。
WEXITSTATUS(*status)
获取子进程返回值//wexitstatus
//拿到的是一个8位的数,占一个字节,
//如:exit(10)为10,exit(-10),打印的结果为246
//若return的值,只是高八位表示。 -
waitpid
pid_t waitpid(pid_t pid, int *status, int options);
功能:阻塞等待子进程退出,回收资源
参数:pid:>0 指定子进程进程号
=-1任意子进程
=0 等待其组ID等于调用进程的组ID的任一子进程
<-1等待其组ID等于pid的绝对值的任一子进程
status
:同上
options
:WNOHANG
:不阻塞
0:阻塞
返回值:正常:结束的子进程的进程号
使用选项WNOHANG
且没有子进程结束时:0
出错:-1
waitpid(-1,NULL,0) ==> wait(NULL);
【7】exec族函数
-
概念:
exec函数族提供了一种在进程中启动另一个程序执行的方法。它可以根据指定的
文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。
在执行完之后,原调用进程的内容除了进程号外,其他全部都被替换了。 -
何时使用exec?
- 当进程认为自己不能再为系统和用户做出任何贡献了时就可以调用exec函数,让自己执行新的程序
- 如果某个进程想同时执行另一个程序,它就可以调用fork函数创建子进程,然后在子进程中调用任何
一个exec函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
#if 0
l : list列表,参数要以列表形式,展现出来。
p : path路径,从系统环境变量中去找可执行文件
v : 向量数组,将列表的参数装到数组中去。
e : 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,
子进程使用当前程序的环境。envp也是一个以NULL结尾的字符串数组指针
#endif
- 使用区别:
- 可执行文件查找方式
无p:指定完整的文件目录路径,
有p:只给出文件名,系统会自动从环境变量“$PATH”所包含的路径中进行查找。 - 参数表传递方式
有l:表示逐个列举的方式;
有v:表示将所有参数构造成指针数组传递,其语法为char *const argv[] - 环境变量的使用
exec函数族可以默认使用系统的环境变量,也可以传入指定的环境变量。
这里,以“e”(Enviromen)结尾的两个函数execle、execve就可以在envp[]中传递
当前进程所使用的环境变量,一般为NULL即可
- 可执行文件查找方式
【8】守护进程的创建
-
创建一个子进程,让父进程退出。
目的:子进程变成后台进程
fork(); -
创建一个新的会话,将进程设置为会话组组长
目的:让进程脱离终端
setsid(); -
修改路径为根目录。
目的:防止进程执行的路径被删除
chdir(); -
重设权限掩码。
umask(); -
关闭文件描述符。
close();