获取进程相关的ID ,fork函数,父子进程(上)(进程控制)【linux】(zj)

获取进程相关的ID

头文件

#include <sys/types.h>
#include <unistd.h>

函数原型:

 pid_t getpid(void);
 pid_t getppid(void);
 uid_t getuid(void);
 gid_t getgid(void);
  • 函数的返回值都是就是上面所获得到的id

功能

getpid函数:获取调用该函数的进程ID
getppid函数:获取调用该函数进程的父进程ID
getuid函数:获取调用该函数的用户ID
getgid函数:获取调用该函数的组ID

  • 函数不会调用失败,永远都是成功的不会调用失败。

getpid函数

获取进程PID
代码:

  #include <stdio.h>
   #include <unistd.h>
   #include <sys/types.h>
   int main()
  {
       pid_t pid = 0;
       pid = getpid();
      printf("%d\n",pid);
       while(1);
      return 0;
 }

运行结果为:

在这里插入图片描述

我们打开新终端进行进程查看:
命令:ps -aux
在这里插入图片描述

  • 我们可以查看到a.out程序运行起来之后生成的pid为4179的进程。

在这里插入图片描述

  • 进程关闭之后,刚才PID为4179的进程将不存在。

getppid函数

代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    pid_t pid = 0;
    pid_t ppid = 0;
    pid = getpid();
    ppid = getppid();
    printf("%d\n",pid);
    printf("%d\n",ppid);
    while(1);
    return 0;
}

运行结果为:
在这里插入图片描述

我们可以看到当前进程的id是4683
当前进程父进程的ID是3702

我们在进程l列表里面进行查看:
在这里插入图片描述

我们可以看到当前进程的PID和当前进程父进程的PID
因为我们在bash终端下面运行的a.out程序
所以a.out进程的父进程就是bash终端

  • 在那个窗口下面运行程序,进程的父进程就是运行所在的窗口。

getuid和getgid

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    pid_t pid = 0;
    pid_t ppid = 0;
    uid_t uid = 0;
    gid_t gid = 0;

    pid = getpid();
    ppid = getppid();
uid = getuid();
gid = getgid();
    printf("%d\n",pid);
    printf("%d\n",ppid);
    printf("%d\n",uid);
    printf("%d\n",gid);
    while(1);
    return 0;
}

运行结果为:
在这里插入图片描述用户id和组id都是1000表示当前进程在ID为1000的用户下面运行,
并且组内只有一个人,所以gid也是1000.

fork函数

程序是如何运行起来的:

  • 1.在内存中划出一片内存空间。
  • 2.将硬盘上的可执行文件中的代码(机器指令)拷贝到划出来的内存空间
  • 3.pc指向第一条指令,CPU取指令运行

有OS以上过程是通过API来实现
Linux平台提供两个非常关键的API

  • exec :
    将程序代码(机器指令)拷贝到开辟的内存空间
    让pc指向第一条指令,进程就开始运行
    运行的进程和其他进程切换并发运行。

  • fork :
    开辟出一块内存空间

函数原型

#include <unistd.h>

pid_t fork(void);

函数功能:
从调用函数的进程中复制出子进程
被复制的进程则成为父进程
复制出来的进程成为子进程

复制之后的结果:
1.依照父进程的内存空间样子,原样复制地开辟出子进程的内存空间
2.子进程原样复制父进程的空间,因此子进程内存空间中的代码和数据和父进程完全相同

函数无参数

返回值
1,父进程的fork 成功返回子进程的PID 失败返回-1 errno被设置
2,子进程的fork 成功返回0 失败返回-1 errno被设置

代码演示:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        pid_t ret = 0;
        ret = fork();
        printf("%d,ret = %d\n",getpid(),ret);
        return 0;

}

运行结果为:
在这里插入图片描述上面的结果有点奇怪,是因为父进程先结束了。

上面的结果是由于父进程在调用fork之后产生产生子进程也会调用fork

上面结果中:
5122是父进程进程的PID
5123是父进程调用fork产生的子进程返回的子进程PID
5123是在自己子进程里面运行的getpid得到的PID
0是子进程调用fork函数返回值。

我们给代码加上循环不让其退出来查看两个进程:
在这里插入图片描述

上面打印的结果都比较正常,
因为父进程和子进程都进入到了while循环没有退出。
在这里插入图片描述
我们可以看到两个PID都是a.out他们都是从a.out演变而来的。

如何让父子进程做不同的事情?

我们就可以根据fork函数的返回值来进行区分:
父进程fork返回子进程PID 子进程fork返回值为0

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
    pid_t ret = 0;
    ret = fork();
    if(ret > 0)
    {   
        printf("this is parent PID = %d\n",getpid());
        printf("parent ret = %d\n",ret);
    }   
    else if(ret == 0)
    {   
        printf("this is child PID = %d\n",getpid());
        printf("child ret = %d\n",ret);
    }   
    printf("hello world\n");
    while(1);
    return 0;
}

运行结果为:
在这里插入图片描述

进程复制的原理

Linux有虚拟内存机制,父进程运行在虚拟内存上,虚拟内存是OS通过数据结构基于物理内存模拟出来的,因此底层对应的还是物理内存。复制进程的时候,会复制父进程的虚拟内存数据结构,就得到了子进程的虚拟内存,相应的底层就会对应的一段新的物理内存空间,里面放了与父进程一模一样的代码和数据。我们的目的是得到子进程的物理内存空间,然后调用exec去加载新程序的代码,去执行新程序的代码。在没有使用exec加载之前父子进程拥有相同的代码。

父子进程各自会执行那些代码?

图示:
在这里插入图片描述

注意:子进程调用fork函数返回的0并不是PID

验证子进程复制了父进程的代码和数据

我们对于代码进行修改:

 #include <stdio.h>
   #include <sys/types.h>
   #include <unistd.h>
int main()
   {
       pid_t ret = 0;
       printf("before fork\n");
       ret = fork();
     if(ret > 0)
      {
          printf("this is parent PID = %d\n",getpid());
          printf("parent ret = %d\n",ret);
      }
      else if(ret == 0)
      {
          printf("this is child PID = %d\n",getpid());
          printf("child ret = %d\n",ret);
      }
      printf("after fork\n");
     while(1);
      return 0;
  }

执行结果为:
在这里插入图片描述子进程并没有输出before fork 但是输出了after fork 说明子进程复制了父进程的代码但是子进程是从fork位置开始执行,fork前面的代码不执行。

我们在对于代码进行修改:

#include <stdio.h>
   #include <sys/types.h>
   #include <unistd.h>
int main()
   {
       pid_t ret = 0;
       printf("before fork\n");
       ret = fork();
     if(ret > 0)
      {
          printf("this is parent PID = %d\n",getpid());
          printf("parent ret = %d\n",ret);
      }
      else if(ret == 0)
      {
          printf("this is child PID = %d\n",getpid());
          printf("child ret = %d\n",ret);
      }
      printf("after fork\n");
     while(1);
      return 0;
  }

上面代码我们只是去掉before fork后面的\n
运行结果为:
在这里插入图片描述我们可以看到父进程和子进程都打印了before fork ,
我们前面说过,子进程从fork开始运行。
解释说明:
当父进程执行到before fork 的时候后面没有\n就会被保存到printf缓冲区,然后父进程执行fork,就会复制父进程的代码和数据,就会把父进程printf缓冲区的beforefork进行复制。

过程如下:
在这里插入图片描述子进程在执行

printf("this is child PID = %d\n",getpid());

的时候刷新刷新缓冲区,
那么子进程就打印出来了before fork
加\n
在fork运行之前,printf(“before fork\n”); 里面有\n 会刷新printf函数缓冲区,缓冲区不会有数据,所以在父进程执行fork函数之后,子进程也就不会从从父进程的printf函数缓冲区复制到数据。

发布了163 篇原创文章 · 获赞 94 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43648751/article/details/104488873