博主初学Linux进程相关知识,暂时总结到这里,有什么错误问题,欢迎大家指正!
一 进程概述
进程这个东西呢,是操作系统的最核心概念,是操作系统资源管理的最小单位。
Linux操作系统借助于进程来管理计算机的软硬件资源,支持多任务的并行执行。
Linux进程
- 进程是一个动态的实体,是程序是一次执行过程,程序是保存在硬盘上的可执行代码,是静态的。
- 这里顺便提一下线程:线程就是在进程内部,比进程更小的独立运行的基本单位,并且不占资源,它可以创建和撤销一个进程或者线程。
- ps 或者pstree(将所有行程以树状图显示)可以查看系统的进程。
- 实际用户ID(uid):标识运行该进程的用户
- 有效用户(euid):标识以什么用户身份来运行进程。用户A运行了一个身份是root的程序,程序运行时有root权限,此时,实际用户ID是A,有效用户ID是root
- Linux进程分为:
代码段:存可执行代码
数据段:存全局变量,常量,静态变量
堆栈段:存函数,函数内部定义的局部变量
- LInux进程状态:
运行状态:R
中断状态:S
不可中断状态:D
僵死状态:Z
停止状态:T
- 后缀:
<:高优先级进程
N:低优先级进程
s:会话首进程
l:多线程进程
+:位于前台进程组
二 进程操作
LInux下对进程主要系统调用:
fork:创建新进程
exit:终止进程
exec:执行一个应用程序
wait:父进程挂起,等待子进程终止
getpid:获取当前进程ID
getppif:返回父进程ID
nice:改变进程优先级
创建进程
1.fork
当fork函数调用成功后,当前进程(父进程)会新创建一个子进程,所以它有两个返回值,
父进程调用fork时返回子进程ID,子进程调用后返回0,异常时返回-1.
我们用fork创建一个进程
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
pid_t pid;
printf("Process Creation Study\n");
pid = fork();
switch(pid){
case 0: //此时为子进程
printf("Chid running,CurPid is %d,ParentPid is %d\n",pid,getppid());
break;
case -1: //出错
perror("Wrong!\n");
break;
default: //此时父进程
printf("Parent is running,ChildPid is %d,ParenrPid is %d\n",pid,getpid());
break;
}
exit(0);
}
细心的我们可以看到上面有getppid()和getpid()这两个长得极端相似的函数,事实上:
getpid返回当前进程标识,getppid返回父进程标识。
运行后:
Process Creation Study
Parent is running,ChildPid is 18867,ParenrPid is 18866
Chid running,CurPid is 0,ParentPid is 18866
其实由于内核所使用的调度算法不同,fork之后子进程和父进程的运行先后顺序是有可能不同的,大家如果有兴趣的话,可以试着多运行几次, 我们可能会有几种不同的输出结果。
事实上,在fork()后父进程和子进程就已经分离两者已经分成两部分,交替执行下面的程序代码
运行下面程序,我们更能直观的看出来父进程和子进程交替运行的情况
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
int pid;
char * msg;
int k;
printf("Process Creation Study\n");
pid = fork(); //父子进程分裂
switch(pid){
case 0:
msg = "Child running";
k = 3;
break;
case -1:
perror("Wrong!\n");
break;
default:
msg = "Parent is running";
k = 5;
break;
}
while(k>0){
puts(msg);
sleep(1);
k--;
}
exit(0);
}
运行一下:
Process Creation Study
Parent is running
Child running
Parent is running
Child running
Parent is running
Child running
Parent is running
Parent is running
几点补充:
1.子进程会继承父进程包括用户,用户组ID,当前工作目录等,但它有自己唯一的进程ID
2.子进程共享父进程打开的文件描述符,但父进程的文件描述符改变不会影响子进程中的文件描述符
3.子进程不继承父进程设置的文件锁,警告
4.子进程未决信号集被清空
2.孤儿进程
如果父进程先于子进程结束,子进程就会成为孤儿进程(可真是形象呢)
成为孤儿进程后,正常来讲,此时的孤儿进程会由init进程收养,此时子进程ID为1。
其实不然!
详见我的另一篇博客:https://blog.csdn.net/weixin_43204126/article/details/96871493
3.vfork函数
1.fork下的子进程复制父进程的资源,自身独立,拥有良好并发性,但也因为其要复制过多资源,有时是并不需要的,这就徒增了系统调用开销
2.vfork下的子进程会直接和父进程共享地址空间,节省了不必要的开销
3.fork下的进程运行先后顺序取决于系统的调度算法,而vfork保证子进程先运行,如果在调用exit或exec使得父进程运行前,子进程要依赖父进程的某个行为,就会造成死锁
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int globvar = 0;
//int var = 0;
int main(){
pid_t pid;
int i;
int var = 0;
printf("fork is different with fork\n");
//pid = fork();
pid = vfork();
switch(pid){
case 0:
i = 3;
while(i--){
printf("Child is running\n");
globvar++;
var++;
sleep(1);
}
//exit(0);
//}
printf("child's globvar = %d,var = %d\n",globvar,var);
exit(0); //必须加这个,不然不正常退出子进程会导致父进程var乱码
break;
case -1:
perror("something wrong!\n");
exit(0);
default:
i = 5;
while(i--){
printf("Parent is running\n");
globvar++;
var++;
sleep(1);
}
printf("Parent's globvar = %d,var = %d\n",globvar,var);
exit(0);
}
}
运行结果:
fork is different with fork
Child is running
Child is running
Child is running
child's globvar = 3,var = 3
Parent is running
Parent is running
Parent is running
Parent is running
Parent is running
Parent's globvar = 8,var = 8
当我们注释掉pid = vfork();,让//pid = fork();加入程序时,会有下面的运行结果:
fork is different with fork
Parent is running
Child is running
Child is running
Parent is running
Child is running
Parent is running
Parent is running
child's globvar = 3,var = 3
Parent is running
Parent's globvar = 5,var = 5
可以很直观的看出其区别
注:使用vfork要谨慎,最好不要让子进程修改父进程的全局和局部变量