Linux软件编程(4)进程

Linux软件编程(4)进程 最近学了Linux软件编程中进程的部分相关内容进程和线程这一部分定义比较多有的比较难以理解建议搞懂、多背定义。一、程序和进程很多人会把程序和进程弄混其实两者很好区分。程序是存放在磁盘上的代码文件属于静态的放在那里不会自己运行。进程是程序跑起来之后的动态过程包含创建、调度、消亡完整生命周期。程序运行就生成进程。每个进程都有自己独一无二的标识 PIDPPID代表它父进程的ID用来描述进程之间的父子关系。二、进程常用命令写代码之前先要学会在终端观察进程。top实时查看CPU占用看进程运行状态ps -ef查看全部进程能看到PID、PPIDpstree以树状打印直观看到父子进程关系ps -aux查看进程详细运行状态杀死进程kill -9 进程PIDkillall -9 进程名后台运行程序在命令末尾加上./a.out jobs查看当前终端下后台任务fg 编号把后台任务切回前台运行修改进程优先级nice -n 优先级 程序renice -n 优先级 进程PID三、进程虚拟内存空间32位系统每个进程拥有0‑4G虚拟内存空间分成几个段文本段存放我们写的指令代码数据段字符串常量、全局变量、静态变量。分为初始化和未初始化两部分系统数据段堆、栈。堆用于动态内存malloc栈保存局部变量、函数调用。关键点每个进程拥有独立虚拟地址空间。但是全部进程共享0‑4G虚拟内存由操作系统做分时复用。两个进程看着地址一样实际对应的物理内存不是同一块。四、进程调度与进程状态系统同时跑很多进程依靠调度算法分配CPU时间。常见调度算法先来先执行、高优先级调度、时间片轮转、多级队列反馈、负载均衡。时间片CPU分配给单个进程一小段执行时间。宏观上多个程序同时运行微观上CPU交替执行同一时刻CPU只跑一个任务。进程的7种状态就绪态R任务已经准备好了等待分配CPU运行态R正在CPU上面执行可唤醒等待态S缺资源主动挂起收到信号可以被唤醒不可唤醒等待态D等待过程不能被信号唤醒停止态T进程被暂停僵尸态Z代码已经执行完毕但是资源没有被父进程回收结束态X资源已经回收进程彻底消失五、进程编程核心函数fork()pid_t fork(void);功能创建子进程。返回值规则一定要记牢父进程返回新创建子进程的PID大于0子进程返回0创建失败返回‑1调用fork之后会复制一份父进程地址空间父子两个进程从fork之后的代码分别往下执行。注意子进程不会从头重新执行main从fork返回处继续运行。如果子进程不写return/exit退出子进程还会继续执行后面的代码甚至继续fork生成大量多余子进程。getpid() / getppid()getpid() 获取当前自己进程PIDgetppid() 获取父进程PIDexit()void exit(int status);结束当前进程。main函数里面exit()和return效果基本一致普通函数内部return只是函数返回不会结束进程想要结束进程必须调用exit。ps、僵尸进程怎么产生如何避免产生原因子进程代码执行结束但是父进程没有回收子进程资源。子进程就变成僵尸进程ZPID还占着内存没有释放。解决办法父进程先退出子进程变成孤儿进程会被系统init进程收养由init帮忙回收。父进程调用wait()主动回收子进程这是我们写代码最常用的方式。七、wait函数pid_t wait(int *wstatus);功能回收子进程资源具备阻塞特性。如果子进程还没结束父进程会卡在wait这里原地等待。返回值成功返回被回收子进程PID失败返回‑1。参数wstatus用来保存子进程退出状态配合一组宏使用WIFEXITED(wstatus) 判断子进程是否正常退出WEXITSTATUS(wstatus) 获取子进程退出返回值WIFSIGNALED(wstatus) 判断进程是不是被信号杀死WTERMSIG(wstatus) 获取杀死进程的信号编号八、waitpid函数我们知道子进程退出之后如果父进程不去回收它子进程就会变成僵尸进程仍然占用系统资源。waitpid就是用来回收指定子进程的系统调用。pid_t waitpid(pid_t pid, int *wstatus, int options);参数说明pid想要回收的子进程PIDwstatus指针用来保存子进程退出状态信息传NULL代表不关心退出状态options选项0阻塞回收。子进程没有退出父进程就卡在这一行一直等子进程结束。WNOHANG非阻塞回收。如果子进程还没结束函数直接返回0父进程不会卡住可以继续干别的事。返回值成功返回被回收的子进程pid失败返回-1设置WNOHANG时如果子进程还没有结束返回0wait只能随便等待任意一个子进程waitpid可以精准指定回收哪一个子进程还能选择阻塞或者非阻塞处理僵尸进程更加灵活。九、exec函数族进程内部执行另一套程序fork创建子进程之后子进程复制父进程的代码。如果我们想要子进程去跑一个完全不一样的程序就需要exec函数族。exec不会创建新进程它复用当前进程的PID直接把原来的代码替换成新程序的代码。执行成功之后后面的代码全部不会再运行。一共有6个exec系列函数名字后缀有含义llist参数以列表形式逐个传最后必须以NULL结尾vvector参数放到指针数组里面传进去p会去系统环境变量PATH路径里面查找程序文件不用写完整路径e可以自己传入自定义环境变量int execl(const char *path, const chararg, …/(char *)NULL */);int execlp(const char *file, const chararg, …/(char *)NULL */);int execle(const charpath, const chararg, …/,(char)NULL, char *const envp[] */);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[], char *const envp[]);返回值特点exec执行成功没有返回值原程序代码直接被覆盖只有执行失败的时候才会返回‑1继续运行后面代码。区分带p不用写完整路径依靠PATH找程序不带p必须写程序完整路径。带l一个个罗列参数带v把参数放到字符串数组。带e可以自定义环境变量。十、环境变量环境变量就是进程运行时临时存放配置信息的变量。比如我们经常见到的PATH就是环境变量系统靠它寻找可执行程序。环境变量是继承关系父进程的环境变量默认会复制给子进程。exec执行新程序的时候默认沿用原有环境变量如果使用带e后缀的exec函数可以传入一套全新环境变量。setenv 修改环境变量int setenv(const char *name, const char *value, int overwrite);参数name环境变量名字value要设置的值overwrite是否覆盖旧值非0如果这个变量已经存在覆盖原来的值0已经存在则不做修改保留旧值返回值成功返回0失败返回‑1。这部分是进程编程的重点写fork子进程调用exec执行外部程序搭配waitpid回收是非常经典的代码模型。