Day28、进程的管理(创建、退出)、子进程

来源:互联网 发布:21天学通c语言视频 编辑:程序博客网 时间:2024/06/05 08:28

一、进程的管理

1、什么是进程?

进程是程序运行的一个实例。

PCB(进程控制块)

pid:进程号

2、Linux进程的组织管理方式

在linux操作系统中,所有的用户进程组成了一棵树,进程和进程之间的关系是父子关系,或兄弟关系。

tarena@tarena-virtual-machine:~$ pstree

init─┬─NetworkManager─┬─dhclient

     │                ├─dnsmasq

     │                └─2*[{NetworkManager}]

     ├─accounts-daemon───{accounts-daemon}

     ├─acpid

     ├─atd

    ├─avahi-daemon───avahi-daemon

init  1号进程   是进程树的树根

pstree (命令) :列出所有用户进程

ps –aux  (命令):查看当前进程的详细信息

3、怎么获取进程的pid

getpid  (2)系统调用:系统调用是用内核来提供服务

#include<sys/types.h>

#include<unistd.h>

pid_tgetpid(void);

pid_tgetppid(void);

功能:获取进程的进程号

参数:

返回值:

举例:pid.c

  1#include<stdio.h>

  2#include<sys/types.h>

  3#include<sys/unistd.h>

  4int main(void){

 5     pid_t pid;

 6     pid=getpid();

 7    printf("pid=%d\n",pid);

 8     getchar();

 9     return 0;

 10 }

tarena@tarena-virtual-machine:~$ gcc pid.c

tarena@tarena-virtual-machine:~$ ./a.out

pid=3154

 

tarena@tarena-virtual-machine:~$ ps -aux

tarena@tarena-virtual-machine:~$ gcc pid.c-E -o getpid.i  (查看typedefine  pid_t的类型)

tarena@tarena-virtual-machine:~$ pstree

     ├─gnome-terminal─┬─bash───pstree

     │                ├─bash───a.out

bash是pstree和a.out的父进程

4、创建一个子进程

fork (2)

#include<unistd.h>

pid_tfork(void);

功能:创建一个子进程

返回值:

在父进程中,子进程的pid被返回

在子进程中,0被返回

-1代表fork系统调用失败。errno被设置

注意:当fork调用结束时,内核就已经有两个进程(父与子),两个PCB

两个进程都运行这个程序往下走,先执行父进程还是子进程不一定

fork 执行后,父进程和子进程执行同一段代码

子进程还是父进程先执行是异步的。(谁先谁后执行不确定,系统调用决定)

       举例:fork.c

         1 #include<stdio.h>

  2#include<unistd.h>

  3int main(){

 4     pid_t pid;

 5     pid=fork();//此时,已经有两个进程

 6     if(pid<0){   //创建子进程失败

 7        perror("fork");

 8         return 1;

 9     }

 10    if(pid= =0){// 子进程创建成功

 11        printf("pid=%d\n",getpid());//打印子进程的pid

 12     }

 13    else{//如果不加else父进程和子进程都执行下一条语句,加上后只有父进程执行

 14    printf("mypid=%d\n",getpid());//父进程的pid

 15     }

 16    return 0;

 17 }

tarena@tarena-virtual-machine:~$ ./a.out

mypid=3227

pid=3228

       总结:当fork成功调用完毕的时候,子进程复制了一份父进程的PCB,这时父子进程只有pid不同,每个进程有自己的一份PCB

(创建出子进程后,父子进程都运行以下的程序)

  1#include<stdio.h>

  2#include<unistd.h>

  3int main(){

 4     pid_t pid;

 5     pid=fork();//此时,已经有两个进程

 6     if(pid<0){   //创建子进程失败

 7        perror("fork");

 8         return 1;

 9     }

 10    if(pid==0){// 子进程创建成功

 11        printf("pid=%d\n",getpid());//打印子进程

 12     }

 13    else{

 14     printf("mypid=%d\n",getpid());//父进程

 15     }

 16    getchar();

 17    return 0;

 18 }

tarena@tarena-virtual-machine:~$ ./a.out

mypid=3235

pid=3236

tarena@tarena-virtual-machine:~$ ps –aux

tarena   3235  0.0  0.0  2008   284 pts/4    S+  02:26   0:00 ./a.out

tarena   3236  0.0  0.0  2008    60 pts/4    S+  02:26   0:00 ./a.out

tarena   3238  0.0  0.2  6368  1172 pts/3    R+  02:27   0:00 ps –aux

tarena@tarena-virtual-machine:~$ pstree

     ├─gnome-terminal─┬─bash───pstree

     │                ├─bash───a.out───a.out

练习:用进程创建子进程,在子进程再创建子进程

  1#include<stdio.h>

  2#include<unistd.h>

  3int main(){

 4     pid_t pid1,pid2,pid3;

 5     pid1=fork();

 6     if(pid1<0){

 7        perror("fork");

 8         return 1;

 9     }

 10    if(pid1==0){

 11        printf("pid1=%d\n",getpid());//打印子进程

 12        pid2=fork();

 13        if(pid2<0){

 14            perror("fork");

 15            return 1;

 16        }

 17        if(pid2= =0){

 18            printf("pid2=%d\n",getpid());//打印子进程

 19             pid3=fork();

 20            if(pid3<0){

 21                 perror("fork");

 22                 return 1;

 23            }

24             if(pid3= =0){

 25                printf("pid3=%d\n",getpid());//打印子进程

 26            }

 27        }

 28     }

 29    else{

 30    printf("父进程pid=%d\n",getpid());

 31     }

 32    getchar();

 33    return 0;

 34 }

tarena@tarena-virtual-machine:~$ ./a.out

父进程pid=3273

pid1=3274

pid2=3275

pid3=3276

 

tarena@tarena-virtual-machine:~$ pstree

     ├─gnome-terminal─┬─bash───pstree

     │                ├─bash───a.out───a.out───a.out───a.out

 

 

5、进程的同步(同步指的是有顺序,一个进程执行完另一个执行)

wait/waitpid    (2)系统调用  

wait是阻塞的,只能等待一个子进程

waitpid是非阻塞的,能等待所有子进程

 

wait(2)

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

功能:等待子进程的结束,并获得子进程的状态信息

参数:

status:子进程的退出状态      是值传递(传个地址过去,带值回来)

WIFEXITED(status):用来判断子进程是否正常退出,如果正常退出返回turn。

WEXITSTATUS(status):用来获取到子进程的退出状态码

WIFSIGNALED(status):用来 子进程是不是被信号打断所中断

WTERMSIG(status):获取子进程被信号打断的信号的编号

 

返回值:-1代表错误,若正确,带回子进程的进程号

举例:wait.c

  1#include<stdio.h>

  2#include<sys/wait.h>

  3#include<unistd.h>

  4int main(){

 5     pid_t pid;

 6     int status;

 7     pid=fork();

 8     if(pid<0){

 9        perror("fork");

 10        return 1;

 11     }

 12    if(pid= =0){

 13         printf("子进程pid=%d\n",getpid());//子进程

 14     }

 15    else{

 16        wait(&status);//等待子进程的结束

 17        printf("父进程pid=%d\n",getpid());

 18     }

 19    getchar();

 20    return 0;

 21 } 

tarena@tarena-virtual-machine:~$ ./a.out

子进程pid=3321

(回车)

父进程pid=3320

 

修改wait1.c  扩展功能,获得子进程的状态信息

  1#include<stdio.h>

  2#include<sys/wait.h>

  3#include<unistd.h>

  4#include<stdlib.h>

  5int main(){

 6     pid_t pid;

 7     int status;

 8     pid=fork();

 9     if(pid<0){

 10        perror("fork");

 11        return 1;

 12     }

 13    if(pid==0){

 14        printf("子进程pid=%d\n",getpid());

 15        exit(1);

 16     }

 17    else{

 18        wait(&status);

 19        if(WIFEXITED(status)){//如果正常退出,返回真

 20            printf("status=%d\n",WEXITSTATUS(status));

 21        }

 22        printf("父进程pid=%d\n",getpid());

 23        exit(0);

24     }

 25  /*  getchar();

 26    return 0;*/

 27 }

 

waitpid(2)

pid_t waitpid(pid_t pid, int *status, intoptions);

功能:回收子进程

参数:
pid:指定回收的子进程的子进程号

-1:所有的子进程

0:和父进程同组的子进程

>0:pid指定的子进程

<-1:等待组id等于pid的绝对值的子进程

status:同wait的status参数

WIFEXITED(status):用来判断子进程是否正常退出,如果正常退出返回turn。

WEXITSTATUS(status):用来获取到子进程的退出状态码

WIFSIGNALED(status):用来 子进程是不是被信号打断所中断

WTERMSIG(status):获取子进程被信号打断的信号的编号

options:

0:阻塞

WNOHANG:非阻塞

返回值:若失败,返回-1

举例:waitpid.c (非阻塞)

  1#include<stdio.h>

  2#include<sys/wait.h>

  3#include<unistd.h>

  4#include<stdlib.h>

  5int main(){

 6     pid_t pid;

 7     int status;

 8     pid=fork();

 9     if(pid<0){

 10        perror("fork");

 11        return 1;

 12     }

 13    if(pid==0){

 14        printf("子进程pid=%d\n",getpid());

 15        sleep(30);

 16        exit(1);

 17     }

 18    else{

 19        pid_t r_pid=waitpid(-1,&status,WNOHANG/*非阻塞*/);//等待结束再执行

 20        if(r_pid==-1){

 21            perror("waitpid");

 22            return 3;

 23        }

24        //如果正常退出,返回真。exit(3)

 25        if(WIFEXITED(status)){

 26            printf("status=%d\n",WEXITSTATUS(status));

 27        }

 28        printf("父进程pid=%d\n",getpid());

 29        exit(0);

 30     }

 31  /*  getchar();

 32    return 0;*/

 33 }

tarena@tarena-virtual-machine:~/day28$./a.out

父进程pid=3232

子进程pid=3233

 

若改成

20              pid_tr_pid=waitpid(-1,&status,0 /* 阻塞*/);//等待结束再执行

tarena@tarena-virtual-machine:~/day28$ ./a.out

子进程pid=3245

 

在等待状态。      以上两个代码对比阻塞和非阻塞的区别

6、进程的退出:  exit on_exit   at_exit  _exit

exit:进程退出

return:只是函数的返回。如果在main函数中,return相当于exit退出

#include <stdlib.h>

void exit(int status);

0377  1111,1111  一个字节  0~255   只要后八位

 

atexit(3)

#include <stdlib.h>

int atexit(void (*function)(void));

参数:

void (*function)(void)

参数的名字:function

参数的类型是 :*

回调函数:函数里有一个形参,这个形参又是一个函数的入口地址。在这个参数寄存的那个函数里,可以回调其传进来的函数

回调函数

可以当做实际参数使用的函数叫做回调参数

将一个函数的地址作为实参传递给另一个调用函数

举例:

  1#include<stdio.h>

  2#include<stdlib.h>

  3//回调函数的定义

  4 void func(void){

 5     printf("this is atest\n");

  6 }

  7int main(void){

 8     //向exit函数注册atexit函数

 9     atexit(func);

 10    exit(0);

 11 }

 

 

 

on_exit(3)

#include <stdlib.h>

int on_exit(void (*function)(int , void *),void *arg);

 

  1#include <stdio.h>

  2#include <stdlib.h>

  3//回调函数的定义

  4void func(void)

  5 {

 6     printf("this is apig\n");

  7 }

  8

  9void onfun(int val,void *p)

 10 {

 11    char *buf=(char *)p;

 12    printf("val=%d\tbuf=%s\n",val,buf);

 13 }

 14

 15int main(void)

 16 {

 17    char *str="tang";

 18    //向exit函数注册atexit函数

 19    atexit(func);

 20    on_exit(onfun,str);

 21    exit(3);

 22 }

 

程序执行到exit时会回调atxit和on_exit

tarena@tarena-virtual-machine:~/day28$./a.out

val=3      buf=tang

this is a pig

 

_exit (2)

#include <unistd.h>

void _exit(int status);

exit (3)调用了_exit(2)  系统调用 调用了 库函数

举例说明:

fopen(3)(FILE *)-----àOPEN(2)(fd) ----àclose(fd)---àfclose(3)

 

 

7、孤儿进程和僵尸进程?

什么是孤儿进程?

父进程早于子进程结束,这个子进程就叫孤儿进程

在这种情况下,子进程的父进程由init进程代理(孤儿院)

举例:

  1#include<stdio.h>

  2#include<sys/wait.h>

  3#include<unistd.h>

  4#include<stdlib.h>

  5int main(){

 6     pid_t pid;

 7     int status;

 8     pid=fork();

 9     if(pid<0){

 10        perror("fork");

 11        return 1;

 12     }

 13    if(pid==0){

 14        printf("子进程pid=%d\n",getpid());

 15        sleep(20);

 16     }

 17    else{

 18        printf("父进程pid=%d\n",getpid());

 19        exit(0);

 20     }

 21  /*  getchar();

 22    return 0;*/

 23 }

tarena@tarena-virtual-machine:~/day28$./a.out

父进程pid=2895

子进程pid=2896

tarena@tarena-virtual-machine:~/day28$pstree

init─┬─NetworkManager─┬─dhclient

     │                ├─dnsmasq

     │                └─2*[{NetworkManager}]

     ├─a.out

 

僵尸进程:zombies

子进程结束的时候,父进程需要调用wait来获取子进程的退出状态,并且回收一些信息。

如果子进程结束了,父进程没有调用wait来及时地清理信息,在这个瞬间,子进程的状态就是僵尸状态。

  1#include<stdio.h>

  2#include<sys/wait.h>

  3#include<unistd.h>

  4#include<stdlib.h>

  5int main(){

 6     pid_t pid;

 7     int status;

 8     pid=fork();

 9     if(pid<0){

 10        perror("fork");

 11        return 1;

 12     }

 13    if(pid==0){

 14        printf("子进程pid=%d\n",getpid());

 15        exit(1);

 16     }

 17    else{

 18        sleep(30);

 19        printf("父进程pid=%d\n",getpid());

 20        wait(NULL);

 21     }

 22  /*  getchar();

 23     return 0;*/

24 }

tarena@tarena-virtual-machine:~/day28$ ./a.out

子进程pid=2922

父进程pid=2921

tarena@tarena-virtual-machine:~$ ps –aux

tarena    2992  0.0 0.0   2000   284 pts/0   S+   06:46   0:00 ./a.out

tarena    2993  0.0 0.0      0     0 pts/0   Z+   06:46   0:00 [a.o] <defunct>

 

 

判断大端小端:

  1 #include<stdio.h>

  2 int main(void){

  3     short var_s=0x0001;    // 00000001

  4     char *p=(char *)&var_s;    p指向短整型的一个字节

  5     if(*p){

  6         printf("小端\n");

  7     }

  8     else{

  9         printf("大端\n");

 10     }

 11 }

小端

 

计算机一般是小端,低字节放在低地址 左边高地址,右边是低地址 0000001

网络传输一般是大端,低字节放在高地址,信号按序列发送  


0 0
原创粉丝点击