怎样做自己介绍网站,网站建设公司重庆,广州网站建设公司嘉御,wordpress 版面进程 进程的概念进程的特点如下进程和程序的区别LINUX进程管理 getpid()getppid() 进程的地址空间虚拟地址和物理地址进程状态管理进程相关命令 ps toppstreekill 进程的创建 并发和并行fork() 父子进程执行不同的任务创建多个进程 进程的退出 exit()和_exit() exit()函数让当…进程 进程的概念进程的特点如下进程和程序的区别LINUX进程管理 getpid()getppid() 进程的地址空间虚拟地址和物理地址进程状态管理进程相关命令 ps toppstreekill 进程的创建 并发和并行fork() 父子进程执行不同的任务创建多个进程 进程的退出 exit()和_exit() exit()函数让当前进程退出,并且刷新缓冲区_exit() 进程的等待 wait()函数 和 waitpid()函数 wait()函数 创建⼀个⼦进程, 延时 3s 后退出, ⽗进程等待⼦进程退出 waitpid()函数 函数原型函数参数 进程的替换 在 Linux 系统中提供了⼀组⽤于进程替换的替换共有 6 个函数示例:通过 execl 函数族执⾏ ls -l 命令 进程
进程的概念
进程Process是操作系统对一个正在运行的程序的一种抽象。它是系统运行程序的最小单位是资源分配和调度的基本单位。
进程的特点如下 进程是⼀个独⽴的可调度的活动, 由操作系统进⾏统⼀调度, 相应的任务会被调度到cpu 中进⾏执⾏ 进程⼀旦产⽣则需要分配相关资源同时进程是资源分配的最⼩单位
进程和程序的区别
程序是静态的它是⼀些保存在磁盘上的指令的有序集合没有任何执⾏的概念进程是⼀个动态的概念它是程序执⾏的过程包括了动态创建、调度和消亡的整个过程并⾏执⾏ : 表示多个任务能够同时执⾏依赖于物理的⽀持⽐如 cpu是4核⼼则可以同时执⾏4个任务并发执⾏ : 在同⼀时间段有多个任务在同时执⾏由操作系统调度算法来实现⽐较典型的就是时间⽚轮转
LINUX进程管理
在 Linux 系统中管理进程使⽤树型管理⽅式
每个进程都需要与其他某⼀个进程建⽴ ⽗⼦关系, 对应的进程则叫做 ⽗进程Linux 系统会为每个进程分配 id , 这个 id 作为当前进程的唯⼀标识, 当进程结束, 则会回收
进程的 id 与 ⽗进程的 id 分别可以通过 getpid() 与 getppid() 来获取
getpid()
#include unistd.hpid_t getpid(void);该函数用来获取当前进程的 id
getppid()
#include unistd.hpid_t getppid(void);该函数用来获取当前进程的⽗进程的 id
进程的地址空间 ⼀旦进程建⽴之后, 系统则要为这个进程分配相应的资源, ⼀般系统会为每个进程分配 4G 的地址空间 4G 的进程地址空间主要分为两部分: 0 - 3G : ⽤户空间 3G - 4G 内核空间 ⽤户空间⼜具体分为如下区间 stack : 存放⾮静态的局部变量 heap : 动态申请的内存 .bss : 未初始化过的全局变量包括初始化为 0 的, 未初始化过的静态变量 (包括初始化为 0) .data : 初始化过并且值不为 0 的全局变量, 初始化过的不为 0 静态变量 .rodata : 只读变量字符串之类 .text : 程序⽂本段包括函数符号常量 当⽤户进程需要通过内核获取资源时, 会切换到内核态运⾏, 这时当前进程会使⽤内核空间的资源⽤户需要切换到内核态运⾏时, 主要是通过 系统调⽤
虚拟地址和物理地址
虚拟地址 : 程序运行时使用的地址, 由操作系统管理, 程序只能通过虚拟地址访问内存物理地址 : 实际物理内存中存储数据的地址, 由硬件管理, 程序只能通过物理地址访问内存虚拟地址和物理地址的转换关系由操作系统完成, 程序只能通过虚拟地址访问内存虚拟地址空间和物理地址空间的映射关系由操作系统完成, 程序只能通过虚拟地址访问内存
在 cpu 中有⼀个硬件 MMU(内存管理单元) , 负责虚拟地址与物理地址的映射管理以 及虚拟地址访问 操作系统可以设置 MMU 中的映射内存段 在操作系统中使⽤虚拟地址空间主要是基于以下原因: 直接访问物理地址, 会导致地址空间没有隔离, 很容易导致数据被修改 通过虚拟地址空间可以实现每个进程地址空间都是独⽴的操作系统会映射到不⽤的 物理地址区间在访问时互不⼲扰. 进程状态管理
进程是动态过程操作系统内核在管理整个动态过程时会使⽤了 状态机,
给不同时间节点设计⼀个状态通过状态来确定当前的过程进度在管理动态过程时使⽤状态机是⼀种⾮常好的⽅式 进程的状态⼀般分为如下 运⾏态 (TASK_RUNNING) : 此时进程或者正在运⾏或者准备运⾏, 就绪或者正在进⾏都属于运⾏态睡眠态 () : 此时进程在等待⼀个事件的发⽣或某种系统资源 可中断的睡眠 (TASK_INTERRUPT) : 可以被信号唤醒或者等待事件或者资源就绪 不可中断的睡眠 (TASK_UNTERRUPT) : 只能等待特定的事件或者资源就绪 停⽌态 (TASK_STOPPED) : 进程暂停接受某种处理。例如gdb 调试断点信息处理。僵死态TASK_ZOMBIE进程已经结束但是还没有释放进程资源 进程相关命令
ps
参数:
-e 显示所有进程-f 显示进程详细信息-l 显示进程详细信息包括线程信息-u 显示指定用户的进程-aux 显示所有进程包括其他用户的进程
ps -aux
ps -ef | grep 进程名 # 查找进程top
实时显示系统中进程的运行状态
top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]选项:
d : 改变显示的更新速度或是在交谈式指令列 (interactive command) 按 sq : 没有任何延迟的显示速度如果使⽤者是有 superuser 的权限则 top 将会以最⾼的优先序执⾏c : 切换显示模式共有两种模式⼀是只显示执⾏档的名称另⼀种是显示完整的路径与名称S : 累积模式会将⼰完成或消失的⼦进程 (dead child process) 的 CPU time 累积起来s : 安全模式将交谈式指令取消, 避免潜在的危机i : 不显示任何闲置 (idle) 或⽆⽤ (zombie) 的进程n : 更新的次数完成后将会退出 topb : 批次档模式搭配 “n” 参数⼀起使⽤可以⽤来将 top 的结果输出到档案内 top - 14:34:29 up 7 days, 18:51, 1 user, load average: 1.00, 0.95, 0.61 top名称14:34:29 系统当前时间up 7 days, 14:30系统以及运⾏的时间和 uptime 命令相等1 users当前有 1 个⽤户在线load average: 1.00, 0.95, 0.61系统负载即任务队列的平均⻓度。 三个数值分别为 1 分钟、5 分钟、15 分钟前到现在的平均值。 Tasks: 290 total, 2 running, 287 sleeping, 0 stopped, 1 zombie Tasks任务也就是进程290 total当前总共有 290 个任务也就是 290 个进程2 running2 个进程正在运⾏287 sleeping263 个进程正在休眠0 stopped0 个停⽌的进程1 zombie1 个僵⼫进程 %Cpu(s): 51.0 us, 0.7 sy, 0.0 ni, 47.8 id, 0.0 wa, 0.0 hi, 0.5 si, 0.0 st %Cpu(s)CPU 使⽤率51.0 us⽤户空间占⽤ CPU 时间的百分⽐⼤部分进程都运⾏在⽤户态通常都是希望⽤户空间 CPU 越⾼越好0.7 sy内核空间占⽤ CPU 时间的百分⽐Linux 内核态占⽤的 CPU 时间系统 CPU 占⽤越⾼表明系统某部分存在瓶颈。通常这个值越低越好0.0 ni占⽤ CPU 时间的百分⽐ni 是 nice 的缩写进程⽤户态的优先级如果调整过优先级那么展示的就是调整过 nice 值的进程消耗掉的 CPU 时间如果系统中没有进程被调整过 nice 值那么 ni 就显示为 047.8 id空闲 CPU 占⽤率等待进程运⾏0.0 wa等待输⼊输出的 CPU 时间百分⽐CPU 的处理速度是很快的磁盘 IO 操作是⾮常慢的。wa 表示 CPU 在等待 IO 操作完成所花费的时间。系统不应该花费⼤量的时间来等待 IO操作否则就说明 IO 存在瓶颈0.0 hiCPU 硬中断时间百分⽐硬中断是硬盘、⽹卡等硬件设备发送给 CPU 的中断消息 0.5 siCPU 软中断时间百分⽐软中断是由程序发出的中断 0.0 st被强制等待involuntary wait虚拟 CPU 的时间此时 Hypervisor 在为另⼀个虚拟处理器服务。 MiB Mem : 3889.9 total, 366.0 free, 1535.2 used, 1988.6 buff/cache MiB Mem内存3889.9 total物理内存总量366.0 free空闲内存量1535.2 used已使⽤的内存量1988.6 buff/cache⽤作内核缓存的内存量 MiB Swap: 2048.0 total, 2035.2 free, 12.8 used. 2082.9 avail Mem MiB Swap交换空间虚拟内存当内存不⾜的时候把⼀部分硬盘空间虚拟成内存使⽤2048.0 total交换区总量2035.2 free空闲交换区总量12.8 used使⽤的交换区总量2082.9 avail Mem可⽤于启动⼀个新应⽤的内存物理内存和 free 不同它计算的是可回收的 page cache 和 memory slab PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND PID进程 idUSER进程所有者PR进程的优先级越⼩优先级越⾼NInice 值负值表示⾼优先级正值表示低优先级VIRT进程使⽤的虚拟内存单位是 kbRES进程使⽤的物理内存单位 kbSHR进程使⽤的共享内存单位 kbS进程状态S 表示休眠R 表示正在运⾏ Z 表示僵死状态N 表示该进程优先值为负数I 表示空闲状态%CPU进程占⽤ CPU 时间的百分⽐%MEM进程占⽤内存的百分⽐TIME进程实际运⾏的时间COMMAND进程的名称 pstree
显示进程树 kill
kill 命令是⽤于结束进程的命令或者⽤于显示相关信号
kill [选项] [参数]
选项:
-l 显示信号名称-s 指定发送的信号-a 杀死进程组中的所有进程-p 杀死进程组中的进程并将它们从进程组中剔除-u 指定用户-signal 发送指定的信号
参数:
进程号 要结束的进程号
进程的创建
并发和并行 并发多个任务在同⼀时间段被调度运行⽐如同时有两个任务在运行这就是并发。在有限的 cpu 核⼼的情况下如只有⼀个 cpu 核⼼) , 利⽤快速交替 (时间⽚轮 转) 执⾏来达到宏观上的同时执⾏ 并行多个任务在不同时间段被调度运行⽐如同时有两个任务在不同 CPU 上运行这就是并行。在 cpu 多核的⽀持下实现物理上的同时执⾏ 并⾏是基于硬件完成⽽并发则可以使⽤软件算法来完成, 在完成任务时可以创建多个进程并发执⾏ fork()
创建子进程
返回值:
0 子进程-1 出错 创建子进程的过程:父进程 fork() 系统调用创建子进程返回子进程的进程号子进程复制父进程的地址空间子进程从 fork() 系统调用返回父进程继续执行 ⽗⼦进程并发执⾏, ⼦进程从 fork() 之后开始执⾏ ⽗⼦进程的执⾏顺序由操作系统算法决定的不是由程序本身决定 ⼦进程会拷⻉⽗进程地址空间的内容, 包括缓冲区、⽂件描述符等 (COPY ON WRITE) 父子进程执行不同的任务
使用 fork() 创建子进程⽗进程和⼦进程可以并发执⾏不同的任务执行不同的任务需要利用fork()函数返回值
#include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.h
int main(void)
{pid_t cpid;cpid fork();if (cpid -1){perror([ERROR] fork());exit(EXIT_FAILURE);}else if(cpid 0){// 子进程printf(Child process task.\n);exit(EXIT_SUCCESS);}else if (cpid 0){// 父进程printf(Parent process task.\n);}//父子进程都要执行的代码printf(Child and Process Process task.\n);
return 0;
}创建多个进程
在创建多个进程时, 最主要的原则为 由⽗进程统⼀创建统⼀管理, 不能进⾏递归创建
#include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.hint main(){int cpid;cpid fork();if (cpid -1){perror(fork(): );exit(EXIT_FAILURE);}else if (cpid 0){// 子进程printf(The child process %d running...\n,getpid());sleep(2);printf(The child process %d has exited\n,getpid());exit(EXIT_SUCCESS);}else if (cpid 0){// 父进程cpid fork();if (cpid -1){perror(fork(): );}else if (cpid 0){printf(The child process %d running...\n,getpid());sleep(3);printf(The child process %d has exited\n,getpid());exit(EXIT_SUCCESS);}else if (cpid 0){}}return 0;
}
进程的退出 在进程结束时需要释放进程地址空间 以及内核中产⽣的各种数据结构 资源的释放需要通过调⽤ exit 函数或者 _exit 函数来完成 在程序结束时会自动调⽤ exit 函数 exit()和_exit()
exit()函数让当前进程退出,并且刷新缓冲区
#include stdlib.h
void exit(int status);参数:
status进程退出状态系统中定义了 EXIT_SUCCESS 和 EXIT_FAILURE 两个宏用来表示成功和失败的状态码,具体定义在头文件 stdlib.h 中 #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 示例: 创建⼀个⼦进程让⼦进程延时 3 s 后退出 #include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.h
int main(){pid_t cpid;
cpid fork();
if (cpid -1){
perror([ERROR] fork(): );
exit(EXIT_FAILURE);
}else if(cpid 0){printf(Child Process %d running...\n,getpid());
sleep(3);
printf(Child Process %d has exited.\n,getpid());
exit(EXIT_SUCCESS);
}else if(cpid 0){
sleep(5);
}
return 0;
}_exit()
exit 函数与 _exit 函数功能相似, 但有很多不同, 具体如下:
_exit() 属于系统调⽤, 能够使进程停⽌运⾏, 并释放空间以及销毁内核中的各种数据结构exit() 基于_exit() 函数实现, 属于库函数, 可以清理 I/O 缓冲区
进程的等待
在⼦进程运⾏结束后进⼊僵死状态, 并释放资源, ⼦进程在内核中的 数据结构 依然保留⽗进程 调⽤ wait() 与 waitpid() 函数等待⼦进程退出后释放⼦进程遗留的资源 task_struct 结构体随着进程的创建而创建和销毁而销毁, 它包含了进程的所有信息, 包括进程号、进程状态、进程调度信息、进程资源使用信息等 wait()函数 和 waitpid()函数
wait()函数
函数头文件
#include sys/types.h
#include sys/wait.h
pid_t wait(int *wstatus);功能: 让函数调⽤者进程进⼊到睡眠状态, 等待⼦进程进⼊僵死状态后释放相关资源并返回
参数:
wstatus 指向整数的指针用来接收⼦进程的退出状态 获取具体值需要使⽤ WEXITSTATUS() 宏定义 返回:
若成功返回值是⼦进程的进程号若出错返回值是 -1 会阻塞调⽤者进程⼀般为⽗进程 在⼦进程状态为僵死态时回收资源并释放资源后返回 创建⼀个⼦进程, 延时 3s 后退出, ⽗进程等待⼦进程退出
#include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.h
#include sys/wait.h
int main(void)
{pid_t cpid;cpid fork();if (cpid -1){perror([ERROR] fork(): );exit(EXIT_FAILURE);}else if(cpid 0){printf(The Child process %d running...\n,getpid());sleep(3);exit(88);}else if(cpid 0){int rpid,status 0;rpid wait(status);//会阻塞父进程,等待子进程的状态变化,自动释放资源并返回if (rpid -1){perror([ERROR] wait() : );exit(EXIT_FAILURE);}printf(The Child Process %d has exited,exit code %d .\n,rpid,WEXITSTATUS(status));}return 0;
}在 wait 存储在 satus 变量的值, 存储了很多信息, 通过⼀系列 W 开头的宏来解析获取 WIFEXITED(status) : 进程是否正常结束 WEXITSTATUS(wstatus) : 获取进程退出状态值, exit 函数的参数 WIFSIGNALED(wstatus) : 表示该⼦进程是否被信号结束的, 返回真则表示被信号结束的 WTERMSIG(wstatus) : 返回结束该⼦进程的那个信号的信号值 WCOREDUMP(wstatus) : 表示该⼦进程被信号唤醒的 WIFSTOPPED(wstatus) : 表示该⼦进程是否被信号中⽌ (stop) 的 , 返回真则表示是被信号中⽌的 waitpid()函数
waitpid 函数的功能与 wait 函数⼀样但⽐ wait() 函数功能更强⼤, waitpid() 函数可以指定等待的进程
####函数头⽂件
#include sys/types.h
#include sys/wait.h函数原型
pid_t waitpid(pid_t pid, int *wstatus, int options);函数参数 pid : 进程 id -1 : 可以等待任意⼦进程0 : 等待 id 为 pid 的进程 wstatus : 保存⼦进程退出状态值变量的指针 options : 选项 WNOHANG : ⾮阻塞选项 // 若没有可等待的进程, 则返回 0, 否则返回 -1WUNTRACED : 等待被跟踪的进程WCONTINUED : 继续被跟踪的进程WEXITED : 等待退出的进程WSTOPPED : 等待停止的进程WNOWAIT : 不创建新的进程组 #include stdio.h
#include stdlib.h
#include sys/types.h
#include unistd.h
#include sys/wait.h
int main(void)
{pid_t cpid;cpid fork();if (cpid -1){perror([ERROR] fork(): );exit(EXIT_FAILURE);}else if(cpid 0){printf(The Child process %d running...\n,getpid());sleep(3);exit(88);}else if(cpid 0){int rpid,status 0;//rpid waitpid(-1,status,0);//-1是等待任意子进程退出 // 0是默认阻塞的rpid waitpid(-1,status,WNOHANG);//WNOHANG是非阻塞if (rpid -1){perror([ERROR] wait() : );exit(EXIT_FAILURE);}
#if 0//没有子进程退出就返回0while((rpid waitpid(-1,status,WNOHANG)) 0){
}
#endifprintf(The Child Process %d has exited,exit code %d .\n,rpid,WEXITSTATUS(status));}return 0;
}进程的替换
创建⼀个进程后pid 以及在内核中的信息保持 保持不变, 但进程所执⾏的代码进⾏替换 作⽤ : 通过⼀个进程启动另外⼀个进程 应⽤场景:Linux 终端应⽤程序执⾏命令时通过创建⼀个进程后在替换成命令的可执⾏程序再执⾏ 在 Linux 系统中提供了⼀组⽤于进程替换的替换共有 6 个函数
函数原型:
int execl (const char *__path, const char *__arg, ... / (char *) NULL */) pathname 指向一个字符数组即字符串。这个字符串表示可执行文件的路径 arg 指向一个字符数组即命令行参数。这个参数列表可以为空。 省略号 ... 表示可变参数列表它允许传递任意数量的参数给可执行文件包括命令行参数。在参数列表的最后必须以 (char *) NULL 来指示结束。 NULL 是一个字符型指针表示空值用来指示参数列表的结束。int execlp(const char *file, const char arg, … / (char *) NULL */);int execle(const char *pathname, const char arg, … /, (char *) NULL, char *const envp[] */);int execv(const char *pathname, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[], char *const envp[]); 函数参数:path可执文件的路径名file : 可执文件名可以通过 path 环境变量指定的路径arg : 参数列表以 NULL 结尾argv[] : 参数数组envp[] : 环境变量数组函数返回值:成功 : 0失败 : -1示例:通过 execl 函数族执⾏ ls -l 命令
#include stdio.h
#include stdlib.h
#include unistd.h
int main(void)
{int ret;#if 0ret execl(/bin/ls,ls,-l,NULL);//替换当前进程,启动ls命令if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);//退出当前进程}
#endifchar *const argv[]{ls,-l,NULL};//参数列表ret execv(/bin/ls,argv);//替换当前进程,启动ls命令if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);}//env可以查看环境变量ret execlp(ls,ls,-l,NULL);//函数会搜索环境变量当作路径if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);}return 0;
}
//这个操作替换了当前进程,启动了另外的程序
//一般是先创建子进程,然后在子进程中调用 execl 函数族,替换子进程
//替换当前进程,启动ls命令if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);//退出当前进程}
#endifchar *const argv[]{ls,-l,NULL};//参数列表ret execv(/bin/ls,argv);//替换当前进程,启动ls命令if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);}//env可以查看环境变量ret execlp(ls,ls,-l,NULL);//函数会搜索环境变量当作路径if (ret -1){perror([ERROR] execl(): );exit(EXIT_FAILURE);}return 0;
}
//这个操作替换了当前进程,启动了另外的程序
//一般是先创建子进程,然后在子进程中调用 execl 函数族,替换子进程