进程相关接口函数
1、进程
程序:静态的,有序指令的集合
进程:是系统分配资源的总称,是程序执行的一次完整过程(创建、调度、执行、消亡)
(1)程序的组成:
正文段、用户数据段、系统数据段(PCB<进程控制块>、PC、堆栈)
<1>PCB:
- 进程ID
- 用户名、用户组名
- 进程的状态、优先级
- 文件描述符表
<2>PC(程序寄存器):
- 记录程序下一条指令的地址
- 堆栈(栈)
(2)进程的类型
<1>交互进程:和终端相关,可以在前台运行,也可以在后台
<2>批处理进程:和终端无关,可以将指定的进程放在一个工作队列中按顺序执行,一般由系统管理员操作
<3>守护进程:和终端无关,在后台一直循环执行任务
(3)进程的状态
进程相关指令
ps -ef -->查看当前系统进程
ps -aux -->查看当前系统进程,还会1显示当前进程状态
top -->每隔三秒,显示进程的实时信号
<1>运行态(就绪态)–R–正在运行或准备运行的进程
等待态: [区别在于能否被信号打断]
<3>可中断等待态–S–
<4>不可中断等待态–D–
<5>暂停态–T–暂停运行,直到有信号唤醒位置
<6>僵尸态–Z–进程结束后,没有进行资源回收,该进程变为僵尸态
2、进程相关接口函数
1、创建子进程–fork
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
返回值:
成功创建一个新的子进程,父进程返回子进程的PID号,子进程返回0
失败父进程返回-1,没有子进程被创建
PPId-->父进程
getpid()--->获取当前进程的pid
父子进程:
一 个进程通过 fork 函数创建一个新的进程,原本进程称为新进程的父进程,新的进程称为原进程的子进程
子进程会维承父进程中几乎所有数据
(1)、如果父进程优先于子进程结束:
子进程称为孤儿进程,由前台进程变为后台进程,统一由init进程收养
(2)、如果子进程优先于父进程结束:
子进程状态变为僵尸态
一般来说,如果子进程先于父进程结束,子进程应该统一由父进程回收
***子进程在 fork语句的下一条指令开始执行
虽然当父进程结束后系统会自动回收,不过当是一些不方便结束的父进程:
如:服务器、网页等,我们就需要把进程给结束掉,所有接下来时进程的结束。
2、结束进程–exit()/_exit()
#include <stdlib.h>
void exit(int status);
参数:
status:表示进程退出的状态
--------------------------------------------------------
#include <unistd.h>
void _exit(int status);
注:exit函数调用后会刷新所有缓冲区,_exit函数不会刷新
在多个进程中,当一些进程结束时,进程可能出现僵尸态,越积越多最终会导致系统或者服务器崩溃,所有僵尸态是我们必须要避免发生的,下面会给到进程回收的两种方式,看个人习惯,合理使用。
3、进程回收–wait、waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
参数:
wstatus:进程结束时,状态信息的首地址
返回值:
成功返回子进程结束的pid号,失败返回-1;
---------------------------------------------------------
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
pid:进程号,-1(任意的意思,谁先结束先接受谁)
wstatus:进程结束时,状态信息的首地址
options:
0--以阻塞的方式等待子进程结束
WNOHANG--以非阻塞方式等待子进程结束·
4、守护进程
守护进程和终端无关,负责在后台周期性地处理某些事件或等待某些事件响应
(1)进程组
当用户执行一个进程时,就相当于创建了一个进程组,跟该进程具有亲缘关系的所有进程都属于该进程组
(2)会话
当用户打开一个终端时,就创建了一个会话,一个会话由一个或多个进程组组成,一旦终端关闭,该会话中所有进程组中的进程全部结束
(3)守护进程的创建流程:
1、创建子进程,父进程退出
fork();
2、让子进程脱离原本会话
setsid();
3、修改当前工作路径(非必要,但'一般都会改')
chdir("/tmp");
4、重设文件权限掩码(非必要)
umask(0);
5、删除进程中的所有文件描述符
getdtablesize();
eg:
int i=0;
for(i=o;i<getdtablesize;i++)
{
close(i);
}
while(1)
{
周期性的需要执行的事件;
}
5、实例:
创建一个守护进程,在time.log日志文件中,每隔一秒,记录当前时间
分析:因为要持续不断地去记录时间并存在time文件日志中,所以我们可以开两个进程,父进程和子进程,并将子进程挂到后台,而回收父进程的资源空间,使其形成一个保护进程,就可以不断地去记录时间。
代码演示:
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
//struct stat buf;
struct tm *t_log;//定义一个结构体指针
FILE *fp = fopen("time.log","a+");
char arr[100]={0};//定义缓冲区大小
int ret=-1;
int status,end,line=1;
pid_t pid=fork();
time_t t;//定义一个time_t类型的变量,用于时间的输出
if(NULL==fp)
{
perror("open");
exit(-1);
}
memset(arr,0,sizeof(arr));//先给缓冲区清0
while((end=getc(fp))!=EOF)//一行读完是结束
{
if(end=='\n') line++;
}
if(pid==0)//子进程
{
setsid();
while(1)
{
time(&t);
t_log=localtime(&t);//获取时间
sprintf(arr," %d年%d月%d日%d:%d:%d\n",t_log->tm_year+1900,t_log->tm_mon+1,t_log->tm_mday,t_log->tm_hour,t_log->tm_min,t_log->tm_sec);//打印时间,可打印自己想要的的格式
fwrite(arr,sizeof(char),strlen(arr),fp);//写入time.log日至文件
fflush(fp);//刷新
sleep(1);//延迟一秒
}
}
else
{
ret =waitpid(pid,&status,0);//回收进程
if(ret<0)
{
perror("wait");
exit(-1);
}
else//父进程
{
printf("pid=%d\n",pid);
}
}
return 0;
}
FILE *fopen(const char *pathname,const char *mode);
参数:
pathname:打开文件的文件名(包含路径)
mode:打开文件的方式
r:以只读的方式打开文件,文件必须要存在
r+:以读写方式打开,文件必须要存在
w:以只写方式打开文件,如果文件不存在,则会创建再打开
w+:以读写方式打开文件,如果文件不存在,则会创建再打开
a:以只写方式打开文件,如果文件不存在,则会创建再打开
a+:以读写方式打开文件,如果文件不存在,则会创建再打开