Linux:进程控制

进程创建
  • fork函数介绍
    创建子进程,会分配新的内存块和内核数据结构(PCB、地址空间、页表等),将父进程部分数据结构内容拷贝到子进程,添加子进程到系统进程列表中,然后fork返回,开始调度器调度
    fork函数有2个返回值,因为在调用fork后,有两个进程,因此有2个返回值,子进程返回0,父进程返回子进程的pid;
    fork()之后,父子进程代码共享,数据以写时拷贝的方式各自私有一份,父子进程让他们的代码段映射到相同的物理内存的段,因此可以做到代码共享。
    fork调用失败原因:系统中存在太多的进程或者实际用户的进程数超过了限制
    写时拷贝:(1)创建子进程,子进程复制父进程,因此与父进程运行的代码和数据看起来一样,因此子进程的虚拟地址空间和页表和父进程一样,因此父子进程映射之后,指的是同一块物理内存;
    (2)子进程复制了父进程的程序计数器/上下文数据,因此连当前的运行位置都一样;
    (3)若原物理内存数据发生改变,则为了保证进程独立性,因此给子进程重新开辟物理内存,拷贝数据;
  • vfork()函数介绍
    它的作用也是创建子进程,在早期为了提高子进程的创建效率而设计的接口—现在fork有了写时拷贝,因此不怎么常用了,父进程调用vfork之后会阻塞,不再运行,直到子进程调用exit接口退出或者程序替换为止;
    因为vfork创建的子进程与父进程共用同一块虚拟地址空间(两个进程同时运行会造成栈混乱);因此阻塞父进程,子进程不能在main函数中使用return退出,因为会释放所有资源(虚拟地址空间、页表等)
进程终止
  • 进程终止场景
    正常退出:代码运行完毕,结果正常和代码运行完毕,结果不正确
    异常退出
  • 进程常见退出方法
    正常终止:
    1、从main返回(main函数返回值等于当前进程的退出码,可以通过echo $?查看最近进程退出码)
    2、调用exit(库函数)
    3、_exit(系统调用函数)
    例如编写代码
int main()
{
        printf("hello bit!\n");
        return 0;
}

运行后,可以通过echo $?查看它的退出码,例如:

[Daisy@localhost LinuxCode]$ ./myenv
hello bit!
[Daisy@localhost LinuxCode]$ echo $?
0

可以看出它的退出码是0
例如:

[Daisy@localhost LinuxCode]$ ls file
ls: 无法访问file: 没有那个文件或目录
[Daisy@localhost LinuxCode]$ echo $?
2

可以看出退出码是2,代表没有那个文件或目录

  • exit函数
    查看关于它的man手册
#include <stdlib.h>
void exit(int status);
DESCRIPTION 描述
       函数  exit()  使得程序正常中止,status  &  0377  的值被返回给父进程 (参见 wait(2)) 。所有用 atexit() 和on_exit() 注册的函数都以与注册时相反的顺序被依次执行。使用 tmpfile() 创建的文件被删除。
       C 标准定义了两个值 EXIT_SUCCESS 和 EXIT_FAILURE,可以作为 exit() 的参数,来分别指示是否为成功退出。

发现它是用atexit函数注册的。
例如:

int main()
{
        printf("hello bit!\n");
        exit(123);
} 

查看当前退出码例如:

[Daisy@localhost LinuxCode]$ ./myenv
hello bit!
[Daisy@localhost LinuxCode]$ echo $?
123

可以看出exit()函数()中放的是退出码,再比如:

void test()
{
exit(21);
}
int main()
{
        printf("hello bit!\n");
        test();
}

最终结果是:

[Daisy@localhost LinuxCode]$ ./myenv
hello bit!
[Daisy@localhost LinuxCode]$ echo $?
21

可以看出在任何地方调exit函数都表示当前进程退出,return返回值是在main函数中退出,比如:

void test()
{
        exit(21);
}
int main()
{
        printf("hello bit!\n");
        test();
        while(1)
        {

                printf("----------\n");
        }
}

最终运行结果是:

[Daisy@localhost LinuxCode]$ ./myenv
hello bit!

可以看出并没有执行循环,说明是在函数test就退出了
调用exit函数会刷新缓冲区
还有一个_exit函数(一般不建议使用),它的功能与exit函数基本一样,但是也有区别,例如:

int main()
{
        printf("hello bit");
        sleep(3);
        _exit(32);
}

它的结果是:

[Daisy@localhost LinuxCode]$ ./myenv
[Daisy@localhost LinuxCode]$ echo $?
32

发现没有输出结果,说明它是直接退出,不会打印结果,而exi函数首先将所有用atexit函数注册的函数进行调用后再退出。

进程等待

必要性:
子进程退出,父进程如果不管它,就可能在成僵尸进程的问题,造成内存泄漏,进程一旦称为僵尸进程,即使是kill -9也无法杀死;
子进程的退出信息也需要获取

  • 进程等待的方法
    1、wait方法
    例如编写代码:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
int main()
{
        pid_t id=fork();
        if(id==0)
        {//child
                cout<<"I am child....."<<endl;
                sleep(5);
                exit(1);
        }
        else
        {
        		wait(NULL);
                cout<<"I am parent......"<<endl;
        }
        return 0;
}

打开另一个终端,执行

 while :; do ps ajx | grep test | grep -v grep;echo "###########################";sleep 1;done
 让它循环执行这个命令

然后在当前终端开始运行test文件,然后看另一终端,结果是:

[Daisy@localhost ~]$ while :; do ps ajx | grep test | grep -v grep;echo "###########################";sleep 1;done
###########################
###########################
  7264   9982   9982   7264 pts/3      9982 S+    1000   0:00 ./test
  9982   9983   9982   7264 pts/3      9982 S+    1000   0:00 ./test
###########################
  7264   9982   9982   7264 pts/3      9982 S+    1000   0:00 ./test
  9982   9983   9982   7264 pts/3      9982 S+    1000   0:00 ./test
###########################
  7264   9982   9982   7264 pts/3      9982 S+    1000   0:00 ./test
  9982   9983   9982   7264 pts/3      9982 S+    1000   0:00 ./test
###########################
  7264   9982   9982   7264 pts/3      9982 S+    1000   0:00 ./test
  9982   9983   9982   7264 pts/3      9982 S+    1000   0:00 ./test
###########################
  7264   9982   9982   7264 pts/3      9982 S+    1000   0:00 ./test
  9982   9983   9982   7264 pts/3      9982 S+    1000   0:00 ./test
###########################
###########################

这时过了5秒后,子进程先退出,因为父进程调用了wait,如果子进程没有退出,父进程就会一直等,陷入阻塞状态,也就是父进程等了5秒钟,子进程退出,父进程才退出。

2、waitpid方法
例如vim myproc.c:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <sys/wait.h>
  5 #include <sys/types.h>
  6 int main()
  7 {
  8         pid_t id=fork();
  9         if(id==0)
 10         {
 11                 printf("child:pid:%d,ppid:%d\n",getpid(),getppid());
 12                 sleep(5);
 13                 exit(13);
 14                 //child
 15         }
 16         else
 17         {
 18                 printf("father:pid:%d,ppid:%d\n",getpid(),getppid());
 19                 int status=0;
 20                 sleep(10);
 21                 int ret=waitpid(id,&status,0);
 22                 if(ret<0)
 23                 {
 24                         printf("wait error,wait ret:%d\n",ret);
 25                 }
 26                 else
 27                 {
 28                         printf("wait success .......\n");
 29                 }
 30                 sleep(5);
 31                 //father
 32         }
 33         return 0;

打开另一终端,执行

while :; do ps axj | grep myproc | grep -v grep;sleep 1;echo "######################";done

在当前终端make之后,./运行myproc,在另一终端查看,得到:

[Daisy@localhost LinuxCode]$ while :; do ps axj | grep myproc | grep -v grep;sleep 1;echo "######################";done
######################
######################
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 Z+    1000   0:00 [myproc] <defunct>
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 Z+    1000   0:00 [myproc] <defunct>
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 Z+    1000   0:00 [myproc] <defunct>
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 Z+    1000   0:00 [myproc] <defunct>
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
 4273   4274   4273   4083 pts/3      4273 Z+    1000   0:00 [myproc] <defunct>
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################
 4083   4273   4273   4083 pts/3      4273 S+    1000   0:00 ./myproc
######################

分析发现前5秒钟父子进程都在正常运行,都处于S状态,5秒后,子进程成为Z(僵尸)进程,父进程开始等待(只剩下父进程),5秒后父进程退出。
获取子进程
wait与waitpid都有一个status参数,这个参数是一个输出型参数,由操作系统填充
如果传递NULL,表示不关心子进程的退出状态信息,否则,操作系统会根据该参数,将子进程的推出信息反馈给父进程,status当做位图来看待,如图:
在这里插入图片描述
它的低8位表示信号,低7位表示退出时具体收到的信号编号,低8位的倒数第8位表示当前进程是否core dump(核心转储),剩下的次8位表示退出状态(子进程返回值),例如将代码改为:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
        pid_t id=fork();
        if(id==0)
        {
                printf("child:pid:%d,ppid:%d\n",getpid(),getppid());
                sleep(5);
                exit(13);
                //child
        }
        else
        {
                printf("father:pid:%d,ppid:%d\n",getpid(),getppid());
                int status=0;
                sleep(10);
                int ret=waitpid(id,&status,0);
                if(ret<0)
                {
                        printf("wait error,wait ret:%d\n",ret);
                }
                else
                {
                        printf("wait success...:%d\n",ret);
                        printf("exit status...:%d\n",(status>>8)&0XFF);
                        printf("exit signals...:%d\n",status&0X7F);
                }
                sleep(5);
                //father
        }
        return 0;
}

将这个运行起来,打开另一终端,输入

 while :; do ps axj | grep myproc | grep -v grep;sleep 1;echo "######################";done

可以看到

[Daisy@localhost LinuxCode]$ while :; do ps axj | grep myproc | grep -v grep;sleep 1;echo "###################";done
###################
###################
 4731   5252   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
 5252   5253   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
###################
 4731   5252   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
 5252   5253   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
###################
 4731   5252   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
 5252   5253   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
###################
 4731   5252   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
 5252   5253   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
###################
 4731   5252   5252   4731 pts/5      5252 S+    1000   0:00 ./myproc
 5252   5253   5252   4731 pts/5      5252 Z+    1000  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值