Linux编程(三):进程学习笔记
进程
进程概念
- 程序
- 存放在磁盘上的指令和数据的有序集合(文件)
- 静态的
- 进程
- 执行一个程序所分配的资源的总称
- 进程是程序的一次执行过程
- 动态的,包括创建、调度、执行和消亡
进程内容
进程包含正文段、用户数据段、系统数据段。程序包括正文段和用户数据段。
系统数据段主要包括进程控制块、CPU寄存器值、堆栈
- 进程控制块(pcb)
- 进程标识PID
- 进程用户
- 进程状态、优先级
- 文件描述符表
- CPU寄存器
- 保存程序计数器(PC)的值,值为进程中下一指令的地址
进程类型
- 交互进程:在shell下启动。以在前台运行,也可以在后台运行。后台运行则加个&字符。前台可以从终端输入输出,后台不能终端输入但可以输出。
- 批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行
- 守护进程:和终端无关,一直在后台运行。
进程状态
- 运行态:进程正在运行,或者准备运行
- 等待态:进程在等待一个事件的发生或某种系统资源。事件发生或有资源系统去唤醒
- 可中断,等待的过程会不会被信号打断
- 不可中断
- 停止态:进程被中止,收到信号后可继续运行,例如gdb调式
- 死亡态:已终止的进程,但pcb没有被释放
进程相关命令
进程信息的查看
ps -ef
ps aux
查看系统进程信息 ,加|grep 可以通过关键字查找进程名
top
查看进程的动态信息/proc
到该目录下去根据进程号查找目录进入查看进程相关 文件的详细信息,例如cat /proc/6/fd/stat
可以查看进程的状态信息、进程号等
#include <iostream>
using namespace std;
int main()
{
while(1);
return 0;
}
//g++ -o test test.cpp
//./test
ps -ef|more
查看系统进程信息
ps aux
可以查看到系统进程的当前状态
top
查看当前系统的动态信息,查看哪些进程最占资源,方便系统优化查看
进程的优先级
- 普通用户优先级只能大于或等于0,只能降低已有进程的优先级 ,即优先级只能是一个正数
- 管理员用户没有限制,既可以升高优先级也可以降低优先级
- 优先级(-20,19) ,-20优先级最高,默认优先级为0
nice -n 进程等级 ./文件名
以指定的优先级运行进程- renice -n 进程等级 进程ID` 改变正在执行进程的优先级
进程的前后台执行
- 1、后台执行
./文件名 &
执行完毕后显示 后台进程作业号 进程ID - 2、 使用
jobs
查看后台所有运行的进程 - 3、后台进程转换前台运行
fg 作业号
- 4、
Ctrl+z
前台进程变成后台进程挂起,进程状态为变成停止态 - 5、使用
bg 作业号
停止态变成运行态 即将挂起的进程放在后台运行
进程相关函数
进程创建fork
#include <unistd.h>
pid_t fork(void);
- 创建新的进程,失败时返回-1
- 成功时父进程返回子进程的进程号,是一个大于0的值,子进程返回0
- 通过fork的返回值区分父进程和子进程
for example
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
perror("fork");
return -1;
}
else if(pid==0)
{
printf("child process:my pid is %d\n",getpid());
}
else
{
printf("parent process:my pid is %d\n",getpid());
}
}
父子进程
- 子进程继承了父进程的几乎所有的内容,两个不同的进程,pid和ppid不一样
- 父子进程有独立的地址空间,互不影响 ,独立的地址空间、代码和数据,各自的代码和变量
- 若父进程先结束
- 子进程成为孤儿进程,被init进程收养
- 子进程变成后台进程
- 若子进程先结束
- 父进程如果没有及时回收子进程的返回值和退出状态,子进程的pcb没有释放,子进程变成僵尸进程
- 子进程从何处开始执行?
-
父进程从main函数开始,子进程从fork的下一个语句开始执行
-
子进程继承了父进程的程序计数器,程序计数器存放下一条指令的地址
-
- 父子进程谁先执行?
- 指的是fork后谁先执行,CPU调度决定
- 父进程能否多次调用fork?子进程呢?
- 父进程可以多次创建进程,子进程需要回收,子进程也可以调用fork创建孙进程
进程结束exit/_exit
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
- 结束当前的进程并将status低八位返回 父进程只接受低八位
- exit结束进程时会刷新流缓冲区,_exit不会刷新流缓冲区,注意数据丢失的可能
for example1
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("this process will exit");//输入到缓冲区了
exit(0);//刷新了缓冲区会输出到终端了
printf("never be displayed");
}
for example2
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("using exit...\n");//输出到终端上显示
printf("This is the end");//输入到缓冲区里
exit(0);//刷新了缓冲区会输出到终端了
//_exit(0);//缓冲区未刷新不会显示到终端上
}
exec函数族
- 进程调用exec函数执行某个程序
- 进程当前内容被指定的程序替换
- 实现让父子进程执行不同的程序
- 父进程创建子进程
- 子进程调用exec函数族
- 父进程不受影响
execl/execlp
#include <unistd.h>
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
- 成功时执行指定的程序;失败时返回EOF
- path 执行的程序名称,包含路径
- arg…传递给执行的程序的参数列表,最后一定要NULL结尾
- file 执行的程序的名称,在PATH中查找
for example
执行ls命令,显示/etc目录下所有文件的详细信息
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
perror("fork");
return -1;
}
else if(pid==0)
{
printf("child process:my pid is %d\n",getpid());
if(execl("/bin/ls","ls -a","-l","/etc",NULL)<0)
{
perror("excel");
}
// if(execlp("ls","ls","-a","-l","/etc",NULL)<0)
// {
// perror("excelp");
// }
}
else
{
printf("parent process:my pid is %d\n",getpid());
}
return 0;
}
execv/execvp
#include <unistd.h>
int execl(const char *path,const char *const argv[]);
int execlp(const char *file,const char *const argv[]);
- 成功时执行指定的程序;失败时返回EOF
- arg…封装成指针数组的形式
for example
执行ls命令,显示/etc目录下所有文件的详细信息
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
if((pid=fork())<0)
{
perror("fork");
return -1;
}
else if(pid==0)
{
printf("child process:my pid is %d\n",getpid());
char *arg[]={"ls","-a","-l","/etc",NULL};
if(execvp("ls",arg)<0)
{
perror("execv");
}
}
else
{
printf("parent process:my pid is %d\n",getpid());
}
return 0;
}
system
#include <stdlib.h>
int system("const char *command");
- 成功时返回命令command的返回值;失败时返回EOF
- 在当前进程中自动创建一个子进程,子进程去执行command所包含的程序,父进程等待command命令执行结束后才继续执行
进程回收
- 子进程结束时由父进程回收
- 孤儿进程由init进程回收
- 若没有及时回收会出现僵尸进程,父进程没有回收子进程的PCB,当父进程结束时,子进程成为孤儿进程由init进程回收
wait
#include <unistd.h>
pid_t wait(int *status);
- 成功时返回回收的子进程的进程号;失败时返回EOF
- 若子进程没有结束,父进程一直阻塞
- 若有多个子进程,哪个先结束就先回收
- status指定保存子进程返回值和结束方式的地址
- status为NULL表示直接释放子进程PCB,不接收返回值
for example
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int status;
if((pid=fork())<0)
{
perror("fork");
exit(-1);
}
else if(pid==0)
{
sleep(1);
exit(2);
}
else
{
wait(&status);
printf("%x\n",status);
}
return 0;
}
进程返回值和结束方式
- 子进程通过exit/_exit/return 返回某个值(0-255)
- 父进程调用wait(&status)回收
- WIFEXITED(status) 判断子进程是否正常结束
- WEXISTSTATUS(status) 获取子进程返回值
- WIFSIGNALED(status) 判断子进程是否被信号结束
- WTERMSIG(status) 获取结束子进程的信号类型
waitpid
#include <unistd.h>
pid_t waitpid(pid_t pid,int *status,int option);
-
成功时返回回收的子进程的pid或0;失败时返回EOF
-
pid可用于指定回收哪个子进程或任意子进程
-
status指定用于保存子进程返回值和结束方式的地址
-
option指定回收方式,0表示阻塞,WNOHANG表示非阻塞,返回值为0,表示子进程未结束
-
waitpid(pid,&status,0);阻塞回收进程号为pid的子进程
-
waitpid(pid,&status,WNOHANG);非阻塞回收进程号pid的子进程,返回值为0,表示子进程还未结束
-
waitpid(-1,&status,0);阻塞回收任意子进程,等价于wait
-
waitpid(-1,&status,WNOHANG);非阻塞回收任意子进程
守护进程
- 守护进程(Daemon)是Linux三种进程类型之一
- 通常在系统启动时运行,系统关闭时结束
- Linux系统中大量使用,很多服务程序以守护进程形式运行
守护进程特点
- 始终在后台运行
- 独立于任何终端
- 周期性的执行某种任务或等待处理特定事件
- 使用ps -ef时,进程名称最后一个字母是d,表示是个守护进程
会话、控制终端
-
Linux以会话(session)、进程组的方式管理进程
-
每个进程属于一个进程组,运行程序时会创建一个进程去执行这个程序,进程创建时同时也创建了一个进程组,程序如果创建了子进程,子进程和父进程也属于这个进程组
-
会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话,所有通过该终端运行的进程都属于这个会话,一个会话最多打开一个控制终端
-
控制终端关闭时,所有相关进程会被结束
守护进程创建
-
创建子进程,父进程退出
-
if(fork()>0) { exit(0); }
-
子进程变成孤儿进程,被init进程收养
-
子进程在后台运行
-
-
子进程创建新会话
-
if(setsid()<0) { exit(-1); }
-
子进程成为新的会话组长,不属于原先的终端会话了
-
子进程脱离原先的终端
-
-
更改进程当前工作目录
chdir("/tmp")
权限0777可读可写可执行- 守护进程一直在后台运行,其工作目录不能被卸载,守护进程创建时它的当前工作目录有可能指向任意一个目录,需要把守护进程的当前工作目录指向一个不能卸载的地方
- 重新设定当前工作目录cwd
-
重设文件权限掩码
-
if(umask(0)<0) { exit(-1); }
-
文件权限掩码设置为0
-
只影响当前进程
-
-
关闭打开的文件描述符,子进程继承了父进程打开的文件描述符
-
int i; for(i=0;i<getdtablesize();i++) { close(i); }
-
关闭所有从父进程继承的打开文件
-
已脱离终端,stdin/stdout/stderr无法再使用
-
for example
创建守护进程,每隔1秒钟将系统时间写入文件time.log
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
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);
}
else
{
setsid();
umask(0);
chdir("/tmp");
for(i=0;i<getdtablesize();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);
}
}
return 0;
}