进程的基本概念:
1.程序与进程
程序就这存储在磁盘上的可执行文件,当程序被加载到内存中开始运行时就叫做进程
一个程序可以被多次加载生成多个进程,进程就是出于活动状态的计算机程序
2.进程的分类
进程一般分为三类:交互进程、批处理进程、守护进程
守护进程一般都处于活跃状态,运行在后台,由操作系统在开机时通过启动脚本自动创建
3.查看进程
简单形式: ps 显示当前用户的有终端控制的进程信息
列表形式: ps -auxw 显示所有进程的详细信息
a 所有用户的有终端控制的进程
u 无终端控制的进程
x 显示所有进程的详细信息
w 以更长的列宽显示
USER 进程的属主
PID 进程号
%CPU CPU使用率
%MEM 内存使用率
VSZ 虚拟内存使用字节数
RSS 物理内存使用字节数
TTY 终端设备号 “?”表示无终端控制
STAT 进程状态
O 就绪态,等待被调度
R 运行态,在Linux中没有O,就绪态也用R表示
S 休眠态(可被系统中断,获取资源、收到信号等唤醒进入运行态)
D 睡眠态(只能通过系统唤醒)
T 暂停态,收到SIGSTOP信号进入暂停态,收到SIGCONT信号转入运行态
W 等待内存分页(Linux 2.6 后废除)
x 死亡状态
Z 僵尸(僵死)状态
< 高优先级
N 低优先级
l 多线程进程
s 进程的领导者
l 有内存页被锁在内存中
+ 位于后台的进程组
START 进程启动时间
TIME 进程运行时间
COMMAND 启动经常
4、父进程、子进程、孤儿进程和僵尸进程
一个进程可以被另一个进程创建,创建者叫父进程,被创建者称为子进程,子进程在被父进程启动后会在操作系统的同意调度下同时运行
僵尸进程:该进程已死亡,但它的父进程没有立即回收它的相关资源,该进程就会进入僵尸态
孤儿进程:父进程先于子进程结束,子进程就变成了孤儿进程。孤儿进程会被孤儿院(init 守护进程)领养,init就是孤儿进程的父进程
5、进程标识符
每个进程都有一个非负整数表示的唯一的标识,即进程ID/PID
进程ID在任意时刻内都是唯一的,但是可以重复使用,进程一旦结束,它的ID会被系统回收,并经过短暂时间后才分配给其他新创建的进程,即“延迟重用”
pid_t getpid(void);
功能:获取当前进程ID
pid_t getppid(void);
功能:获取父进程ID
init进程ID永远是1
创建进程:
pid_t fork(void);
功能:创建子进程
返回值:一次调用两次返回,子进程返回0,父进程会返回子进程的ID,当进程的数量超过系统的限制时会创建失败,会返回-1给调用者
该函数调用后父子进程各自独立运行,谁先返回不确定,但是可以通过睡眠确定让哪个进程先执行
可以根据返回值的不同,让父子进程进入不同的分支语句,执行不同的代码
通过fork函数创建的子进程会拷贝父进程的数据(数据段、bss段、堆、栈、IO流缓冲区),与父进程共享代码段,子进程会继承父进程的信号处理方式
通过fork函数创建的子进程可以共享父进程的文件描述符
练习1:为进程创建出4个子进程,再为每个子进程创建出两个子进程
A
a a a a
b b b b b b b b
注意:为了防止因为共享代码fork会导致产生不想要的子进程,所以使用pause睡眠的方法让子进程不受其他fork的影响
pid_t vfork(void);
功能:以加载可执行文件的方式来创建子进程
返回值:子进程返回0,父进程返回子进程的PID
子进程先返回,但是此时子进程并没有创建成功,需要加载一个可执行程序来替换当前子进程的所有资源,替换完成后子进程才算创建成功,此时父进程才能返回
使用exec系列函数加载可执行文件:
extern char **environ;
int execl(const char *path, const char *arg, ...);
path:可执行文件的路径
arg:命令行参数,一般第一个是可执行文件的名字,至少要有一个,以NULL结尾
【execl("a.out","a.out","-l","-s",NULL,"-a")】
int execlp(const char *file, const char *arg, ...);
file:可执行文件名字,会根据PATH环境变量的值查找同名的可执行文件
可以添加PATH的值:
1、vim ~/.bashrc
2、在末尾添加 export PATH=$PATH:/home/ubuntu
3、保存退出后,source ~/.bashrc 生效
4、把可执行文件放入新添加的目录中
arg:数组指针
int execle(const char* path,const char *arg,...,char* const envp[]);
path:可执行文件的路径
arg:数组指针
envp:环境变量表,父进程可以在加载子进程时,把一张环境变量表传递给子进程
int execv(const char *path, char *const argv[]);
path:可执行文件的路径
argv:指针数组
int execvp(const char *file, char *const argv[]);
file:可执行文件名字,会根据PATH环境变量的值查找同名的可执行文件
argv:指针数组
int execvpe(const char *file, char *const argv[],char *const envp[]);
file:可执行文件名字,会根据PATH环境变量的值查找同名的可执行文件
argv:指针数组
envp:环境变量表
注意:exec系统函数执行正常是不会返回的,当加载可执行文件失败时才会返回-1
通过exec系统函数创建的子进程不会继承父进程的信号处理函数,但是能够继承父进程的信号屏蔽集。
fork vfork创建的子进程,都可以使用exec系统函数加载可执行文件