进程概念
程序
存放在磁盘上的指令和数据的有序集合(文件)
静态的
进程
执行一个程序所分配的资源的总称
动态的
进程和程序内容区别
进程包含的内容:
BSS段:存放程序中未初始化的全局变量
数据段:已初始化的全局变量
代码段:程序执行代码
堆(heap):malloc等函数分配内存
栈(stack):局部变量,函数参数,函数的返回值
进程控制块(pcb):PID,进程优先级,文件描述符表
进程类型
交互进程
批处理进程
守护进程。
进程状态
运行态
等待态
停止态
死亡态:
查看进程信息
ps 查看系统进程快照
top 查看进程动态信息
/proc 查看进程详细信息
ps 命令详细参数:
-e:显示所有进程
-l:长格式显示更加详细的信息
-f 全部列出,通常和其他选项联用
表头 | 含义 |
F | 进程标志,说明进程的权限,常见的标志有两个:
|
S | 进程状态。进程状态。常见的状态有以下几种:
|
UID | 运行此进程的用户的 ID; |
PID | 进程的 ID; |
PPID | 父进程的 ID; |
C | 该进程的 CPU 使用率,单位是百分比; |
PRI | 进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行; |
NI | 进程的优先级,数值越小,该进程越早被执行; |
ADDR | 该进程在内存的哪个位置; |
SZ | 该进程占用多大内存; |
WCHAN | 该进程是否运行。"-"代表正在运行; |
TTY | 该进程由哪个终端产生; |
TIME | 该进程占用 CPU 的运算时间,注意不是系统时间; |
CMD | 产生此进程的命令名; |
top 查看进程动态信息
shift +> 后翻页
shift +< 前翻页
top -p PID 查看某个进程
改变进程优先级
nice 按用户指定的优先级运行进程
nice [-n NI值] 命令
NI 范围是 -20~19。数值越大优先级越低
普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。
普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。
只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。
renice 改变正在运行进程的优先级
renice[优先级] PID
jobs 查看后台进程
bg 将挂起的进程在后台运行
fg 把后台运行的进程放到前台运行
ctrl+z 把运行的前台进程转为后台并停止。
./test & 把test程序后台运行
创建子进程:
子进程概念:
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值区分父进程和子进程
要点:
1 子进程只执行fork之后的代码
父子进程执行顺序是操作系统决定的。
子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束,子进程成为孤儿进程,被init进程收养,子进程变成后台进程
若子进程先结束,父进程如果没有及时回收,子进程变成僵尸进程
进程的退出
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
结束当前的进程并将status返回
exit结束进程时会刷新(流)缓冲区
return 和exit的区别
main函数结束时会隐式地调用exit函数,普通函数return是返回上一级。
9.进程的回收
#include <unistd.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,哪个先结束就先回收
status 指定保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int option);
参数:pid
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只 要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
options
options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和 WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
WNOHANG :若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
WUNTRACED: 返回终止子进程信息和因信号停止的子进程信息
wait(wait_stat) 等价于waitpid(-1,wait_stat,0)
exec 函数族
背景:fork创建进程之后,子进程和父进程执行相同的代码,但是在实际开发当中,我们希望父子进程执行不同的代码。
作用:执行指定的程序
#include <unistd.h>
intexecl(const char *path, const char *arg, …);
intexeclp(const char *file, const char *arg, …);
成功时执行指定的程序;失败时返回EOF
path 执行的程序名称,包含路径
arg… 传递给执行的程序的参数列表
file 执行的程序的名称,在PATH中查找
注意:两个函数区别execlp不需要写文件名全路径,在PATH查找,最后一个参数必须用空指针(NULL)作结束,进程当前内容被指定的程序替换,但进程号不变。第0个参数必须要写,虽然它没有使用
守护进程(Daemon Process)
概念:守护进程又叫精灵进程(Daemon Process),它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
特点:始终在后台运行,独立于任何终端,周期性的执行某种任务或等待处理特定事件。它是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
始终在后台运行
独立于任何终端
周期性的执行某种任务或等待处理特定事件
进程组(process group):进程集合,每个进程组有一个组长(leader),其进程ID就是该进程组id
会话(session):进程组集合,每个会话有一个组长,其进程ID就是该会话组ID.
控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的Leader就是控制进程(Controlling Process)
举例:http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。
更简便地创建守护进程: nohup 命令
nohup xxxx &
setsid函数:
pid_tsetsid(void);
成功:返回调用进程的会话ID:失败-1,设置error
调用了setsid函数的进程,即是新的会长,也是新的组长
getsid函数
pid_tgetsid(pid_t pid)
成功:返回调用进程的会话ID,失败-1,设置errno
1.pid为0表示查看当前进程session ID
2.ps ajx命令查看系统中的进程。参数a表示不仅当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列出有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息
3.组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
getpid:pid_tgetpid(void); 获取进程id
getpgid:pid_t getpgid(pid_tpid); 获取进程组id
守护进程创建步骤:
创建子进程,父进程退出
子进程变成孤儿进程,被init进程收养
子进程在后台运行
if(fork()>0) exit(0);
子进程创建新会话
子进程成为新的会话组长
子进程脱离原先的终端
if(setsid()<0) exit(-1);
更改当前工作目录
守护进程一直在后台运行,其工作目录不能被卸载
重新设定当前工作目录cwd
chdir("/"); chdir("/tmp");
重设文件权限掩码
文件权限掩码设置为0
只影响当前进程
if(umask(0))<0 exit(-1);
关闭打开的文件描述符
关闭所有从父进程继承的打开文件
已脱离终端,stdin/stdout/stderr无法再使用
for(int i=0;i<3;i++) close(i);
GDB调试多进程程序
首先用gdb调试程序,编译时必须加-g gcc -g -o fork fork.c
gdb fork进入gdb
start 进入单步运行
run直接跑程序
set follow-fork-modechild 设置GDB调试子进程
setfollow-fork-mode parent 设置GDB调试父进程
setdetach-on-fork on/off 设置GDB跟踪调试单个进程或多个
on: 只调试父进程或子进程的其中一个,(根据follow-fork-mode来决定),这是默认的模式
off:父子进程都在gdb的控制之下,其中一个进程正常调试(根据follow-fork-mode来决定),另一个进程会被设置为暂停状态。
info inferiors 显示GDB调试的进程
inferiors 进程序号(1,2,3....) 切换GDB调试的进程