进程
常用命令及基本介绍
ps -ef 查看所有进程信息 (一般需要配合管道使用)
ps aux 查看进程信息 且显示进程状态
状态:
R 运行态 正在运行或可运行
D 等待态 不可中断
S 等待态 可中断
T 停止态
Z 僵尸态
可追加:
+ 前台运行
< 高优先级进程
N 低优先级进程
top 动态的查看进程的信息及状态 每隔3秒刷新一下 且能根据CPU的占用情况从大到小排序
/proc 目录中存放着一些进程相关文件
status 文件中存放进程信息
fd 目录中 存放进程打开的所有文件的信息
nice -n number +./执行文件名 作用是修改进程的优先级 (number 为设定优先级的大小 范围为-20 ~ 19 19最低)
renice -n number +进程号 作用是一般用于修改已有进程的优先级 (number的作用同上)
普通用户只能降低优先级 不能拉高优先级
ROOT用户 可以降低优先级也可以拉高优先级
jobs 显示当前所有后台进程及信息 并对其编号排序
fg +排序号(jobs的排序号) 把相应的后台进程转成前台进程
Ctrl +z 让前台运行进程转成挂起进程(停止态)
bg +排序号 让挂起进程转成后台运行进程
创建进程
创建进程函数
#include<unistd.h>
pid_t fork(void);
失败返回-1
如果成功 父进程返回子进程的进程号 子进程返回0
int getpid(void) 返回进程号
子进程继承了父进程几乎所有内容 但不是全部
父子进程都有自己独立的空间
若父进程先结束
子进程成为孤儿进程 被init进程收养 (也就是说它的父进程变为init进程)
子进程变成后台进程
若子进程先结束
父进程如果没有及时回收 子进程会变成僵尸进程
子进程是在fork()函数之后的下一条语句开始执行
子进程被创建后 父子进程谁先执行? 系统先调用谁 谁就先执行 (如果没有发生进程调度 则父子进程都有可能先执行)
父进程能否多次调用fork()函数? 可以 理论上不受影响
子进程能否多次调用fork()函数? 可以 理论上不受影响
结束进程
#include<stdlib.h>
#include<unistd.h>
void exit(int status)
void _exit(int status)
都会结束当前进程 并返回status
exit()结束进程时 会刷新缓冲区(流)
exec函数族
进程调用exec函数族执行某个程序
进程当前程序被执行程序替换
让父子进程执行不同的程序
父进程创建子进程
子进程调用exec函数族·
父进程不受影响
#include<unistd.h>
int execl(const char *path, const char *arg, ...)
int execlp(const char *file, const char *arg,...)
成功则执行指定程序 失败则返回EOF
path 执行的程序的名称 包含路径
arg.... 传递给程序的参数
file 执行的程序的名称 不含路径 需要在PATH环境变量中查找
#include<unistd.h>
int execv(const char *path, char *const argv[],...)
int execvp(const char *file, char *const srgv[],....)
成功执行指定程序 失败返回EOF
arg....封装成指针数组的形式
Ps:char *arg[]= {"ls","-a", "-l", "/etc", NULL};
if(execv("/bin/ls", arg) < 0)
{
perror("execv");
}
#include <stdlib.h>
int system(const char *command)
成功返回command命令的返回值 失败返回EOF
函数的实现过程 是 先创建一个子进程 然后system()在子进程中执行命令 父进程需要等待子进程执行完之后才能继续执行
进程回收
子进程结束时 由父进程回收
孤儿进程 由init进程回收
如果没有及时回收 那么就会变成僵尸进程 直到其父进程执行结束 僵尸进程才会被init进程回收
进程回收函数
#include<unistd.h>
pid_t wait(int *status)
成功返回被回收的子进程的进程号 失败返回EOF
如果子进程没有结束 父进程会一直处于阻塞状态
若有多个子进程 哪个先结束 哪个先被回收
status用于保存子进程返回值和结束方式的地址
status 如果为NULL 表示直接释放子进程的pcb 不接收返回值
子进程正常结束的三种方式 exit / _exit / return 这三种都会返回一个值(0-255)
父进程调用wait(status)进行回收
WIFEEXITED(status) 可通过这个宏定义判断这个子进程是否是正常结束
WEXITSTATUS(status) 获取子进程返回值
WIFSIGNALED(status) 判断子进程是否是被信号结束
WTERMSIG(status) 获取结束子进程的信号类型
status这个变量中 第0位到第6位存放子进程结束类型 如果为0 说明正常结束 非零说明被信号结束
第8位到第15位存放子进程结束的返回值
进程回收函数
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int option)
参数pid 是指定回收的对象 (如果不想指定 值设为-1即可)
参数option 是指定回收的方式 0或WNOHANG
option为0 如果子进程尚未结束 父进程则一直处于阻塞状态 等待其结束 并将其回收
option为WNOHANG 如果子进程尚未结束 父进程不会一直阻塞 waitpid会返回0 代表子程序没结束
如果子程序结束了 waitpid会返回进程号 代表回收成功
成功则返回子进程的进程号或0 失败返回EOF
如果返回进程号 说明回收成功
如果返回0 说明子进程还没结束 并不代表回收失败
如果返回EOF 说明没有子进程
守护进程
守护进程通常在系统启动时运行 系统关闭时 结束
在linux系统中大量使用 很多服务程序以守护进程形式运行
特点:
始终在后台运行
独立于任何终端
周期性的执行某种任务或等待处理特定事件
linux以会话和进程组的方式管理进程
每一个进程都属于一个进程组
会话是一个或多个进程组的集合 通常用户打开一个终端时 系统会创建一个会话 所有通过该终端运行的进程都属于这个会话
一个会话最多只能打开一个终端(也可以不打开)
当某个终端结束时 所有相关的进程都会被结束
守护进程的创建
1. 创建子进程 父进程退出
此时该子进程会成为孤儿进程被init进程收养 且在后台运行
并且该子进程依然依附于终端 一旦终端关闭子进程会被结束
(此时进程还不属于守护进程)
2. 在子进程中 创建新会话
实现函数 setsid()
该子进程成为会话组组长
且不再依赖于原先终端
3. 更改当前工作目录
可更改为
chdir("/")(这是根目录 普通用户的权限是只读 可执行 不可写)
或 chdir("/tmp")(所有的用户在该目录下都是可读可写可执行)
更改目录的原因:守护进程一直在后台运行 其工作目录是不可被卸载的 所以创建守护进程时 需 要把子进程的工作目录更改到一个永远不会被修改的目录中
4.重设文件权限掩码
实现函数 umask(number) number为设定的掩码 一般为0 代表不会屏蔽任何权限位
重新设定的掩码只会对当前进程有效 不会影响其他进程
5 关闭打开的文件描述符
因为子进程会继承父进程的一些文件 所以在这里需要把这些文件关闭掉
实现函数 getdtablesize() 作用获取文件个数
int i;
for(i=0; i<getdablesize();i++)
close(i);
已脱离终端 stdin /stdout / stderr 无法使用
Like:
创建守护进程 每隔1秒 将系统时间写入到time.log文件中
code:
int main()
{
pid_t pid;
FILE *fp;
time_t t;
int i;
if((pid = fork() < 0)
{
perror("fork");
exit(-1);
}
else if(pid > 0)
exit(0);
setpid();//创建新会话
umask(0); //重设文件掩码
chdir("/tmp"); // 更改文件目录
for(i =0 ;i < getdablesize(); i++) //关闭继承的父进程的文件
close(i);
if((fp = fopen("time.log", "a")) == NULL)
{
perror("fopen");
exit(-1);
}
while(1)
{
time(&t);
fprintf(fp, "%s", ctime(&t));
fflush(fp);
sleep(1);
}
}