组成
-
程序:
-
数据段:存放程序中已经初始化的全局变量的一块内存区域
-
代码段:存放程序执行代码的一块内存区域
-
BSS段:存放程序中未初始化的全局变量的一块内存区域
-
-
堆:存放进程运行中被动态分配的内存段,malloc申请,free释放
-
栈:程序中临时创建的局部变量(不包括static定义),函数传入的参数、函数返回值
-
进程控制块:
-
进程控制块
-
进程标识 PID
-
进程用户
-
进程状态、优先级
-
文件描述符表 最多默认创建1024个,当前打开的文件
-
类型
-
交互进程:shell下启动,可前台 可后台运行
-
批处理进程:和终端无关,被提交到一个作业队列里以便顺序执行
-
守护进程:和终端无关,后台进程
状态
-
运行、等待、停止、死亡(僵尸)
Shell 操作
查看进程信息:
-
ps:查看进程快照 ps -elf ps -elf|grep + 进程名
-
top:查看进程动态信息 翻页:shift+>/< top -p + PID
-
/proc:查看运行进程详细信息 ls /proc
进程优先级:
-
nice 按用户指定的优先级运行进程
-
nice -n NI + 启动名称
-
nice范围 -20 ~19 数值越大优先级越低
-
普通用户只能调制范围0-19 ,只能调高不能调低,而且只能调整自己的进程
-
只有root用户才可以设置进程NI值为负值,而且可以调整任意进程
-
renice 改变进程优先级
-
renice NI +PID
进程运行:
-
jobs:查看后台进程
-
bg:将挂起的进程在后台运行
-
fg:把后台运行的进程放到前台运行
-
ctrl+z 把前天进程转为后台并停止
-
运行进程 + & 把进程后台运行 ./test &
创建子进程
pid_t fork(void);
-
创建失败返回 -1,创建成功父进程返回子进程进程号,子进程返回0
-
通过pid值来区分父子进程
-
子进程只执行fork之后的代码
-
获取进程号:getpid();
-
获取父进程号: getppid();
-
子进程继承父进程内容
-
父子进程有独立的地址空间,互不影响
-
若父进程先结束,子进程变为孤儿进程,被init进程收养,子进程变为后台进程
-
若子进程先结束,父进程没有及时回收,子进程则变为僵尸进程
-
判断子进程,可通过创建的序号区分哪一个子进程
进程退出:
结束当前进程并返回状态值。
-
exit(); 刷新 流 的缓冲区
-
_exit(); 不刷新
-
main函数执行完,会隐式的调用exit函数,普通函数的return是返回上一级函数
进程回收:
避免僵尸进程
-
pid_t wait(int *status)
-
子进程不退出,会一直堵塞父进程
-
-
WEXITSTATUS(status)、
-
获取子进程返回值
-
-
WIFEXITED(status)
-
判断子进程是否正常结束
-
-
pid_t waitpid(pid_t pid,int *status,int option)
-
pid = -1,等待回收任意子进程,这是功能和wait一样
-
pid > 0,等待回收指定pid子进程
-
option = 0 堵塞
-
option = WNOHANG 子进程状态不发生改变,则waitpid不堵塞
-
wait(status) = waitpid(-1,status,0);
-
exec函数族
-
进程调用exec函数族执行某个程序
-
进程当前内容被指定程序替换
-
实现父子进程执行不同的程序
-
父进程创建子进程
-
子进程调用exec函数族
-
父进程不受影响
-
execl/execlp
程序路径/环境变量
第0个参数必须填上,参数最后一个必须是NULL
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
-
path:可执行文件的路径名字
-
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
-
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
-
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
-
l : 使用参数列表
-
p:使用文件名,并从PATH环境进行寻找可执行文件
-
v:先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数参数。
-
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
-
守护进程
-
后台服务进程
-
生存期较长的进程,通常独立于控制终端(为避免终端所产生的信息所打断)并且周期性的执行某种任务或者等待处理某些发生的事件
-
进程组:进程集合,每个进程组都有一个组长,其进程ID就是该进程组ID
-
会话:进程组集合,每个会话都有一个组长,其进程ID就是该会话组的ID
-
控制终端:每个会话可以有一个单独的控制终端,与控制终端连接的leader就是控制进程
shell指令:nohup xxx &
-
步骤:
1. 创建子进程,父进程退出(子进程变成孤儿进程被init收养,子进程后台运行)
pid = fork();
if (pid > 0) {
exit(0);}
2. 创建新的会话 (子进程成为新的会话组长,子进程脱离原来的终端)
if(setsid() < 0){
exit(-1);}
getpid();getsid();getpgid();
3. 更改当前工作目录(守护进程一直后台运行,其工作目录不能被卸载)
chdir("/")
chdir("/tmp");
4. 重设文件权限掩码(文件权限掩码设置为0,只影响当前进程)
if(umask(0) < 0){
exit(-1);}
5. 关闭打开的文件描述符(关闭所有从父进程继承过来的打开文件,已脱离终端,stdin/stdout/stderr/)无法在使用
for(int i = 0;i < 3;i++){
close(i);}。
fdtablesize = getdtablesize();
for (fd = 0; fd < fdtablesize; fd++)
close(fd);