一.进程
1.进程相关指令
ps -ef:查看当前系统进程
ps -aux:查看当前系统进程,并显示单签进程状态
top:每隔3秒,显示进程的实时信息。动态显示,cpu利用率,内存占用率高的放前面
2.进程的优先级
进程的优先级(NI)取值范围:-20~19 优先级默认值为0,值越小,优先级越高,-20的优先级最高。
普通用户能设置的优先级最高为0.
修改进程的优先级:
nice -n 优先等级 ./可执行文件 在准备运行时,将可执行文件的优先级设为自选的优先等级
eg: nice -n 2 ./a.out 在准备运行时,将a.out的优先级设为2
renice -n 优先等级 进程号 在进程运行阶段,将进程的优先级修改
eg: renice -n 2 5549 将进程号为5549的进程的优先级改为2
3.前后台进程的切换
./a.out 将./a.out在前台执行
./a.out & 将./a.out在后台执行
ctrl c 结束进程
ctrl z 挂起前台进程(此时终端会显示挂起的任务号)
bg 任务号 将后台挂起的进程唤醒,唤醒之后进程会变为后台进程
fg 任务号 将后台的进程换至前台运行
jobs 查看后台任务
二、进程相关接口函数
1.创建子进程--fork
#include <sys/types.h>
#include <unistd.h>pid_t fork(void);
返回值:
成功创建一个子进程,父进程返回子进程的PID号,子进程返回0.通过返回值来规划父子进程执行额代码
失败父进程返回-1,没有子进程创建getpid() 返回当前进程的PID号
父子进程:
一个进程通过fork函数创建一个新的进程,原本进程称为新进程的父进程,新的进程称为原进程的子进程。
子进程在fork语句的下一条指令开始执行
子进程会继承父进程中几乎所有数据(局部变量,文件描述符)
PCB(系统数据段)不一样
1.如果父进程优先于子进程结束
子进程称为孤儿进程,由前台进程变为后台进程,统一由init(编号为1)进程收养
2.如果子进程优于父进程结束,且父进程没有回收子进程资源
子进程会变成僵尸进城(僵尸态)
一般来说,如果子进程先于父进程结束,子进程应该统一由父进程回收
2.结束进程 --exit() / _exit()
#include <stdlib.h>
void exit(int status);
参数:
status:表示进程退出的状态。
#include <unistd.h>
void _exit(int status);参数:
status:表示进程退出的状态。
注意:exit函数调用后会刷新所有缓冲区,_exit函数不会刷新缓冲区
3.进程回收 --wait()/waitpid()
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *wstatus);
参数:
wstatus:进程结束时状态信息的首地址,从这个变量返回
返回值:成功返回结束子进程的PID号,失败返回-1
一个wait结束一个子进程,wait(NULL)不接受子进程的消息💖💖💖wait的位置不同效果不同,wait会阻塞程序
如果想要得到子进程结束信息,可以用以下宏函数来得到:🤷♂️🤷♂️🤷♂️🤷♂️
WIFEXITED(wstatus)---判断一个子进程是否是正常退出,正常退出返回1,非正常退出返回0
WEXITSTATUS(wstatus)---查看一个子进程的返回值
WIFSIGNALED(wstatus)---查看是否是信号导致的结束,此时子进程的返回值为0
WTERMSIG(wstatus)--- 查看信号是多少
#include <sys/types.h>
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
pid:进程号,-1表示接收任意子进程
watatus:进程结束时状态信息的首地址,从这个变量返回
options:
0---表示以阻塞方式等待结束
WNOHANG---以非阻塞方式等待结束。如果没有孩子存在,结束
4.exec函数族
#include <unistd.h>
extern char **environ;l:list列表
v:argv
p:PATH这个函数是没有返回值的,因为从调用该函数开始,用户区就被调用的二进制程序给替换掉了,已经不再受我们控制
可以看到在程序运行时,下面的代码并没有执行.🐱🚀🐱🚀🐱🚀execl,参数: 路径+可执行文件名,列表
execlp,参数为: 可执行文件名,列表 (带p的要先配置环境变量)
execv,参数: 路径+可执行文件名,指针数组首地址 (指针数组要提前定义)
execvp,参数为: 可执行文件名,指针数组首地址
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
参数:
pathname:要执行程序的文件名,包含路径
arg:执行程序的命令行参数
命令行参数列表以NULL结尾 🤳🤳🤳
eg:ls -l, execl("/bin/ls","ls","-l",NULL);
execl("test","./test",NULL);
shell命令多在/bin
返回值:失败返回 -1int execlp(const char *file, const char *arg, .../* (char *) NULL */);
添加环境变量:
在家目录下的.bashrc内加入export PATH=$PATH:文件路径,再用source .bashrc刷新一下,就可以执行。👀👀
参数:
file:要执行的程序名,不需要加路径。
int execv(const char *pathname, char *const argv[]);char *buf[]={"ls","-l",NULL};
execv("/bin/ls",buf);int execvp(const char *file, char *const argv[]);
5.守护进程
守护进程与终端无关(终端关了也在执行),负责在后台周期性的处理某些事件,或者等待某些事件响应
(1)进程组:当用户运行一个进程时,就相当于创建了一个进程组,与该进程具有亲缘关系(父子关系)的所有进程都属于该进程组
(2)会话:当用户打开一个终端时,就创建了一个会话,一个会话由一个或者多个进程组组成。一旦会话结束/终端关闭,该会话中所有进程组全部结束。
守护进程的创建流程:
1.创建子进程,父进程退出
fork(); exit(0);
2.让子进程脱离原本会话
setsid();
3.修改当前工作路径,防止意外(路径丢失)---非必要
改为/ 或者/tmp
chdir()
4.重设文件权限掩码---非必要
umask(0);
5.删除进程中所有的文件描述符
守护进程无输入输出,默认3个自动打开的。
n=getdtablesize();获得当前文件描述符的最大个数
for(i=;i<n;i++)
{
close(i);
}
while(1)
{
周期性需要执行的事件
}
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
#include <unistd.h>
int chdir(const char *path);#include <unistd.h>
int getdtablesize(void);
作业:
创建一个守护进程,在time.log日志文件中,每隔一秒记录当前时间
1.创建一个守护进程
2.守护进程中执行功能
/*===============================================
* 文件名称:daemon.c
* 创 建 者:
* 创建日期:2022年08月08日
* 描 述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
int main(int argc, char *argv[])
{
pid_t pid=fork();
if(pid<0)//出错
{
perror("fork");
}
time_t my_t;
time(&my_t);
struct tm *t=localtime(&my_t);
if(pid==0)//子进程
{
setsid();//脱离原本会话,成为新的回话
chdir("/");//修改文件路径
umask(0);//重设文件掩码
for(int i=0;i<getdtablesize();i++)
{
close(i); //关闭所有的文件描述符
}
while(1)//周期性的实现功能
{
//execl("/home/hqyj/test/IO/day1/a.out","./a.out",NULL);
FILE *fp = fopen("/home/hqyj/test/IO/day4/homework/time.log","a+");
time(&my_t); //更新时间
t=localtime(&my_t);
fprintf(fp,"%04d年%02d月%02d日 %d:%02d:%02d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);//输出到文档中
//fflush(fp);
fclose(fp);
sleep(1);
}
}
else //父进程
{
exit(0); //删除父节点,使子节点变成孤儿节点
}
return 0;
}