进程的相关概念
文章目录
一、程序和进程的区别:
程序:存放在磁盘上的指令和数据的有序集合(文件),是静态的
。
进程:执行一个程序所分配的资源的总称,进程是程序的一次执行过程
,它是动态的
,包括创建、调度、执行和消亡。
提示:以下是本篇文章正文内容,下面案例可供参考
二、进程类型
进程的类型有三种:交互进程、批处理进程、守护进程
。
交互进程:在shell下启动,既可以在前台运行,也可以在后台运行。
批处理进程:和终端无关
,被提交到一个作业队列中以便顺序执行。
守护进程:和终端无关
,一直在后台运行。
ps:进程都是需要占用cpu的,但是那么多进程如果同时都在运行态,那么cpu的资源完全不够用,所以还有停止态、僵尸态(“死掉”的进程,但还没清理)。
三、查看进程信息
- ps :作用是查看系统进程快照
ps为什么显示的进程那么少呢?这是因为它只显示当前shell环境下运行的进程,而不是说linux里就这几个进程。如果要查看linux下所有的进程,可以输入“ps -e”,如下图所示:
在该行中参数e是显示所有进程、参数l是显示更长更详细的信息、参数f是列出全部。
列出的信息表中,表头对应的含义如下表所示:
表头 | 含义 |
---|---|
F | 进程标志,说明进程的权限,常见的标志有两个: 1:进程可以被复制,但是不能被执行; 4:进程使用超级用户权限; |
S | 进程状态。进程状态。常见的状态有以下几种: 1. -D:不可被唤醒的睡眠状态,通常用于 I/O 情况。 2. -R:该进程正在运行。 3. -S:该进程处于睡眠状态,可被唤醒。 4. -T:停止状态,可能是在后台暂停或进程处于除错状态。 5. -W:内存交互状态(从 2.6 内核开始无效)。 6. -X:死掉的进程(应该不会出现)。 7. -Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。 8. -<:高优先级(以下状态在 BSD 格式中出现)。 9. -N:低优先级。 10. -L:被锁入内存。 11. -s:包含子进程。 12. -l:多线程(小写 L)。 13. -+:位于后台。 |
UID | 运行此进程的用户的 ID; |
PID | 进程的 ID; |
PPID | 父进程的 ID; |
C | 该进程的 CPU 使用率,单位是百分比; |
PRI | 进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行; |
NI | 进程的优先级,数值越小,该进程越早被执行; |
ADDR | 该进程在内存的哪个位置; |
SZ | 该进程占用多大内存; |
WCHAN | 该进程是否运行。"-"代表正在运行; |
TTY | 该进程由哪个终端产生; |
TIME | 该进程占用 CPU 的运算时间,注意不是系统时间; |
CMD | 产生此进程的命令名; |
- top:作用是查看进程动态信息,要翻页可以通过shift+>和shift+<进行翻页,按q或ctrl+c退出
如果要特定查看某个进程,可以通过“top -p PID”查看 - /proc:查看进程详细信息,可以“ls /proc”查看,目录对应着进程:
四、子进程
1、子进程概念
子进程就是由另外一个进程(父进程)所创建的进程。需要注意的是,在linux下,除了0号进程外的所有的进程都是由别的进程创建
的,换句话说就是除了0号进程外都有父进程
。
子进程的创建:使用fork,其格式如下:
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1,成功时父进程返回子进程的进程号,子进程返回0。可以通过fork的返回值区分父子进程。
fork相当于把进程A复制了一份,进程B并不执行全部代码,而是只执行fork之后的代码(如果执行全部代码,那么进程B也会有fork,不断创建子进程,最后溢出)
那又有问题来了,既然进程B执行的程序与进程A几乎一样,那为什么又要创建这个子进程呢?
答案就在fork的返回值
,fork返回的值进程A和进程B都有,但两者不相同
,所以可以通过fork的返回值编写条件判断语句
,让父、子进程实现不一样的功能
。
如:
pid = fork();
if(pid > 0)//父进程
{
...
}else if(pid == 0)//子进程
{
...
}else//返回值小于0,错误
{
perror("进程出错!");
}
注意:
- 子进程只执行fork之后的代码
- 父子进程执行顺序是操作系统决定的。
- 子进程继承了父进程的内容
- 父子进程有独立的地址空间,互不影响
- 若父进程先结束
子进程成为孤儿进程
,被init进程收养
,init进程专门收养孤儿
子进程变成后台进程
- 若子进程先结束
父进程如果没有及时回收,子进程变成僵尸进程
结束进程——exit/_exit
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
void _Exit(int status);
·结束当前的进程并将status返回
·exit结束进程时会刷新(流)缓冲区
在main函数中return会隐式调用exit
,其他函数不会。
exit会刷新缓冲区
,但在前面加横杆就不会刷新。
回收子进程
- wait:
#include <sys/wait.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程就会一直阻塞
若有多个子进程,哪个先结束就先回收
status指定保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB(进程控制块),不接收返回值
- waitpid:
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int option);
成功时返回回收的子进程的pid或0;失败时返回EOF
pid可用于指定回收哪个子进程
status指定用于保存子进程返回值和结束方式的地址
option指定回收方式,0或者WNOHANG
五、exec函数族
作用:
- 进程调用exec函数族执行某个程序
- 进程当前内容被指定的程序替换
- 实现
让父子进程执行不同的程序
——子进程调用exec函数族,这不会对父进程造成影响。
exec函数族有点像“夺舍”,进程自杀被其他程序夺舍运行。shell调用我们程序就是通过exec函数族实现的。(它的作用跟“system”有点像)
execl/execlp
#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 <stdio.h>
#include <unistd.h>
int main()
{
if(execl("/bin/ls","ls","-a","-l","./",NULL)<0)
{
perror("exec error!");
}
}
可以看到,我们运行test程序和直接输入命令"ls -a -l ./“一样。
如果是用execlp,就不用把路径(”/bin/ls")写全了,写"ls"就好:
#include <stdio.h>
#include <unistd.h>
int main()
{
if(execlp("ls","ls","-a","-l","./",NULL)<0)
{
perror("exec error!");
}
}
需要注意的是,这两个函数最后一个参数需要写NULL或者是(char *)0
。
补充:
execv§:
execl§功能一致,但是它将命令行合并为一个数组,只需要传入数组便可:
char *arg[]={"ls","-a","-l","/etc",NULL};
if(execv("/bin/ls",arg)<0)
{
perror("execv failed");
}
if(execvp("ls",arg)<0)
{
perror("execvp failed");
}
六、守护进程
“守护进程”这个概念在上文中已经出现过一次,它是Linux三种进程类型之一,是Linux中的后台服务进程
。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
本质上来讲守护进程是个后台进程
,但它跟一般的后台进程还不太一样,它不能通过bg、fg转到前台去,它独立于终端,与终端没有关系
!守护进程脱离终端是为了不被终端所产生的信息打断
。
相关概念:
- 进程组:进程集合,每个进程组有一组长,其进程ID就是该进程组ID
- 会话:进程组集合,每个会话有一个组长,其进程ID就是该会话组ID
- 控制终端:每个会话可以有一个单独的控制终端,与控制终端连接的Leader就是控制进程。
守护进程的创建
- 创建子进程,父进程退出。此时子进程成孤儿,被init进程收养,在后台运行
- 子进程创建新的会话。子进程成为新的会话组长,子进程脱离原先终端
- 更改当前工作目录。(非必须)守护进程一直在后台,其工作目录不能被卸载,所以重新设定。
- 重设文件权限掩码为0。
- 关闭打开的文件描述符。关闭所有从父进程继承的打开文件——stdin(标志输入)、stdout(标准输出)、stderr(标准错误)。(ps:文件描述符0、1、2默认分别对应stdin/stdout/stderr。)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
int main()
{
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork");
return 0;
}else if(pid>0)
{
exit(0);
}
printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
if(setsid()<0)
{
perror("setsid");
exit(0);
}
printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
chdir("/");
if(umask(0)<0)
{
exit(-1);
}
//关闭文件描述符
close(0);
close(1);
close(2);
sleep(100);
}