文档库 最新最全的文档下载
当前位置:文档库 › 【VIP专享】Linux下进程管理

【VIP专享】Linux下进程管理

Linux下进程管理+fork()函数

什么是进程?

运行起来的程序,就是进程,进程在内存中运行

主流的操作系统中,都采用多进程多任务并行。每个进程的内部采用多线程实现并行

在UNIX 系统中,如何查看进程

ps命令可以查看进程 (ps –aux是linux的 unix不支持)

ps – 只能查看当前终端起动的进程

ps –ef :unix/linux通用的查看进程

ps –aux:linux直接支持,unix不直接支持,但可以用whereis ps 定位,然后用/usr/ucb/ps -aux

PPID 父进程

STAT进程状态:

R 运行

S休眠状态;大多数进程 在多数时间都休眠。

s代表这个进程有子进程;

T停止或被追踪;

X 死掉的进程(从来没见过)

Z 僵尸进程(结束但没有回收内存等资源的进程)

如果进程a起动了进程B,a叫b的父进程,b叫做子进程。(有些进程既是父进程,又是子进程)

Unix进程的启动顺序:

系统先启动进程0,进程0 只负责启动进程1(init进程)或者启动进程1和进程2,其他进程都是进程1和进程2启动。

unix用进程的pid管理进程,每个进程有唯一的一个PID,在同一时刻进程的PID不会重复。进程的pid可以重用(延时重用,类似手机号)

PID的本质是一个整数,类似文件描述符。

几个常用函数“

getpid() 取当前用户的ID

getppid() 取父进程的ID

getuid()/geteuid() 取当前用户的ID(有效用户)

启动子进程:

1.fork()—复制父进程启动子进程

2.vfork()+execl() –启动全新的子进程

fork()创建子进程(简单的复杂函数)

无参,返回子进程的PID

fork()通过复制父进程创建子进程

会复制除了代码区外所有的内存区域间,代码区会共享

fork()之前的代码只有父进程执行一次,fork()之后的代码,父子进程都执行一次(执行2次)。

1 #include

2 #include

3 int main(){

4 printf("begin\n");

5 pid_t pid=fork();//fork()函数有两次返回,父进程返回子进程的PID,子进程返回0

6 printf("end %d %d\n",getpid(),pid);

7 return 0;

8 }

begin

end 11159 0

end 11158 11159~

~ fork的中文意思是叉子渔叉如图(真的很形象)

~

fork()创建子进程后,父子谁先运行不确定(随系统不同),谁先结束不确定

fork()创建子进程时,如果父进程有文件描述符,子进程会复制文件描述符,但不复制

文件表。

父子进程的关系

fork()之后,父子进程同时运行,如果子进程先结束,子进程给父进程发一个信号,父

进程负责回收子进程资源。

fork()之后,父子进程同时运行,如果父进程先结束,子进程变成孤儿进程,会认进程

1(init)做新的父进程,init进程叫孤儿院。

fork()之后,父子进程同时运行,如果子进程发信号是出现了问题,或者父进程没有及

时处理信号,子进程就会变成僵尸进程。

父进程的缓冲区也会被复制到子进程。

./fork.c

1 #include

2 #include

3 int main(){

4 printf("begin\n");

5 pid_t pid=fork();

6 if(pid==-1)perror("fork"),exit(-1);

//fork()出错原因:

1.系统进程总数有限额。

2.用户进程总数有限额。

fork()失败返回-1,结果是子进程创建失败,父进程继续运行

fork()出错很难,一般是中毒了,一般情况下不会超额,所以可以不判断-1;

7 //练习:父子进程打印不同的内容

8 if(pid==0){

9 printf("2 \n");

10 printf("%d %d\n",getpid(),getppid());//子进程得其父进程

11 }

12 else{

13 printf("1 %d %d\n",getpid(),pid);//父进程的子进程直接用fork()返回值pid

14

15 }

16 return 0;

17 }

~ ----------------------------------------------------------------------------------------------------- ./fork2.c

1 #include

2 #include

3 #include

4 #include

5 int i1=10;

6 int main(){

7 int i2=10;//i2在fork()之前,所以子进程会复制

8 char*str=malloc(10);

9 strcpy(str,"abcd");

10 pid_t pid=fork();

11 int i3=10;//i3 父子进程分别创建

12 if(!pid){

13 i1=20;i2=20;i3=20;

14 str[0]='1';

15 printf("child :i1=%d,i2=%d,i3=%d,str=%s\n",i1,i2,i3,str);

16 printf(" &i1=%p,&i2=%p,&3=%p,str=%p\n",&i1,&i2,&i3,str);

17 exit(0);//子进程结束,不再运行。

18 }

19 sleep(1);

20 printf("father:i1=%d,i2=%d,i3=%d,str=%s\n",i1,i2,i3,str);

21 printf(" &i1=%p,&i2=%p,&3=%p,str=%p\n",&i1,&i2,&i3,str);

22

23 }

child :i1=20,i2=20,i3=20,str=1bcd

&i1=0x8049798,&i2=0xbfc6b3c4,&3=0xbfc6b3c0,str=0x9289008

father:i1=10,i2=10,i3=10,str=abcd

&i1=0x8049798,&i2=0xbfc6b3c4,&3=0xbfc6b3c0,str=0x9289008

父子进程地址一样,但每个进程都会创建0——4G的内存空间

父子进程地址一样,其实映射的物理内存不一样。

结论:子进程会复制出了代码外的其他内存区域,子进程有自己独立的空间

研究文件描述符与文件表

./fork3.c

1 #include

2 #include

3 #include

4 #include

5 int main(){

//pid_t pid=fork();//父子进程新建描述符//先fork后open 看看有什么区别???

6 int fd=open("a.txt",O_RDWR|O_CREAT,0666);

7 if(fd==-1)perror("open"),exit(-1);

8 pid_t pid=fork();//复制描述符不复制文件表

9 if(pid==0){

10 printf("child:fd=%d\n",fd);

printf("%d\n",lseek(fd,0,SEEK_CUR)); //偏移量为0

11 write(fd,"abc",3);

printf("%d\n",lseek(fd,0,SEEK_CUR));// 偏移量为3

12 close(fd);//关闭子进程的fd与文件表的对应关系

13 exit(0);

15 printf("father:fd=%d\n",fd);

printf("%d\n",lseek(fd,0,SEEK_CUR));//先open偏移量为3 先fork偏移量为0

16 write(fd,"123",3);

printf("%d\n",lseek(fd,0,SEEK_CUR));// 先open偏移量为6 先fork偏移量为3

17 close(fd);// 关闭子进程的fd与文件表的对应关系

18 }

结果:child:fd=3 father:fd=3; cat a.txt abc123

~ 一个进程会有一个文件描述符总表(系统分配)。父子进程分别有一个总表,

父进程已经open建了一个文件表并有在总表中建立fd与文件表的映射。

fork()后,子进程复制文件描述符,所以子进程的fd继承自父进程,child:fd=3 但不会复制文件表,就是说:父子进程共用一个文件表,共享读取位置偏移量,所以cat a.txt=abc123;

如果先fork()再open:cat a.txt=123;偏移量分别独立。

lseek 所有打开的文件都有一个当前文件偏移量(current file offset (cfo))用lseek可改变cfo

练习:验证:如果父进程先结束,子进程会以init进程做新的父进程

思路:父进程先sleep ,运行子进程,子进程打印此时的父进程PID,然后子进程在sleep(),父进程结束后,子进程再次打印父进程的PID。

1 #include

2 #include

3 #include

4 int main(){

5 printf("ppid=%d\n",getpid());

6 pid_t pid=fork();

7 if(pid==-1)perror("fork"),exit(-1);

8 if(pid==0){

9 printf("%d\n",getppid());

10 sleep(2);

11 printf("%d\n",getppid());

12 }else{

13 sleep(1);

14 exit(0);

15 }

16 return 0;

17 }

[root@localhost 2.18]# a.out

19882

[root@localhost 2.18]# 1

结果解释:父进程的父进程是终端shell。父进程结束后,发信号给shell,shell回收父进程资源。而shell和字进程没关系,不会帮子进程调回[root@localhost 2.18]#

进程的就结束/终止:

进程可能是正常结束也可能开始非正常结束

正常结束进程的方法:(自己导致结束)

1.主函数中运行return语句

2.函数exit()可以终止进程

3.exit()/Exit()可以终止进程

4.最后一个线程结束

5.主线程结束

非正常结束进程的方法:(被其他的东西弄死了)

1.信号被终止

2.主线程被其他线程取消

return和exit()区别;

return是用来退出函数的,exit()是退出进程的。

exit()/_exit()/_Exit()的区别;_Exit()→ stdlib _exit()/_Exit()在底层是一样的,没区别,调用时头文件不一样_exit()→ unistd exit()不会立即结束进程,可以调用atexit(),注册过的函数之后在结束进程

_Exit() 会立即结束进程。。送走子进程到init,告诉父进程;

atexit(函数指针) 容许进程在结束进程之前调用其他函数,但如果用_exit 不会调用

./exit.c

1 #include

2 #include

3 void fa(){

4 printf("fa is callde\n");

5 }

6 int main(){

7 atexit(fa);//只是注册fa,不会立即调用

8 printf("begin\n");

9 exit(0);//参数退出码用来记录退出情况//_Exit()立即结束,不调用fa.

10 printf("end\n");

11 }

(注:如果没exit(),主函数最后会自动return 0;此时会调用fa;

~

相关文档