如何判断一个程序是进程

$ ps axj | head -1 ; ps axj | grep myprocess
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 8032  9193  9193  8032 pts/2     9193 S+    1004   0:00 ./myprocess

获得进程的pid值

使用getpid函数获得进程本身的id

使用<sys/types.h><unistd.h>

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

int main()
{
  while(1)
  {
    printf("I am a process, pid: %d\n", getpid());
    sleep(1);
  }
  return 0;
}

使用getppid获得父进程的pid

使用<sys/types.h><unistd.h>

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

int main()
{
  while(1)
  {
    printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
    sleep(1);
  }
  return 0;
}

何为父进程?

Linux系统中,新的进程,往往通过父进程的方式创建出来的

$ ./myprocess 
I am a process, pid: 9986, ppid: 8032
$ ./myprocess 
I am a process, pid: 9997, ppid: 8032
$ ./myprocess 
I am a process, pid: 10001, ppid: 8032

可以发现,父进程一直不变

查询发现,该进程是-bash

$ ps axj | head -1 && ps axj | grep 8032
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 8031  8032  8032  8032 pts/2    10138 Ss    1004   0:00 -bash

结论:自己写的程序,会交给命令行解释器创建进程

-bash(命令行解释器)是命令行中执行所有程序的父进程

如何创建子进程?

创建子进程,本质是OS内部多了一个子进程(task struct + 代码和数据)

但是这个过程是一个系统调用

使用fork进行创建子进程

NAME
       fork - create a child process

SYNOPSIS
       #include <unistd.h>

       pid_t fork(void);
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
  fork();
  printf("I am a process(fork), pid: %d, ppid: %d\n", getpid(), getppid());
  sleep(1);

  return 0;
}
$ ./myprocess 
I am a process, pid: 15728, ppid: 8032
I am a process(fork), pid: 15728, ppid: 8032
I am a process(fork), pid: 15729, ppid: 15728

可以发现,后面一句printf居然被打印了两次

因为原本的进程15728在fork();这句时创建了一个子进程15729

此时,两个进程会从fork();这行继续往下执行

因此出现了这种打印结果

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

int main()
{
  printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
  fork();

  while(1)
  {
    printf("I am a process(fork), pid: %d, ppid: %d\n", getpid(), getppid());
    sleep(1);
  }
  return 0;
}

bash是怎么创建子进程的?

$ which bash
/usr/bin/bash
$ ldd /usr/bin/bash
    linux-vdso.so.1 =>  (0x00007fff1d9ec000)
    libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007ffaf79f2000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007ffaf77ee000)
    libc.so.6 => /lib64/libc.so.6 (0x00007ffaf7420000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ffaf7c1c000)

可以发现bash是用c语言写的,因此内部也是通过fork创建子进程

/proc文件夹中查看进程

$ ls /proc/
1      12     17065  2016   22977  25238  26366  278    296    38   5001  541   595   7475  9021  buddyinfo  devices      fs          keys        mdstat   pagetypeinfo  softirqs       timer_stats
10     12328  17374  20986  22987  25699  26377  28     297    386  501   5726  596   771   9029  bus        diskstats    interrupts  key-users   meminfo  partitions    stat           tty
10387  13     18     21     23     25702  27     280    29732  39   509   5733  5973  8     904   cgroups    dma          iomem       kmsg        misc     sched_debug   swaps          uptime
10395  14     19     22     232    25703  270    28881  3      47   51    5745  5994  835   9040  cmdline    driver       ioports     kpagecount  modules  schedstat     sys            version
10406  16     19920  22605  24     25747  271    289    36     49   52    577   65    837   927   consoles   execdomains  irq         kpageflags  mounts   scsi          sysrq-trigger  vmallocinfo
1089   16640  2      22641  25     26     272    28900  365    5    536   580   7     839   96    cpuinfo    fb           kallsyms    loadavg     mtrr     self          sysvipc        vmstat
11     16659  20     22663  25226  26359  275    29     37     50   538   584   7468  9     acpi  crypto     filesystems  kcore       locks       net      slabinfo      timer_list     zoneinfo

其中,新建一个进程就会新建一个文件夹(名称同pid)

进程消失后,文件夹消失

文件夹内部是各种属性的记录,其中一个重要属性是cwd(currect work diratore)

这个属性会记录当前进程所处的工作路径(可执行文件的路径)

编程时所说的当前路径就是通过cwd来获取的

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

int main()
{
  FILE *fp = fopen("log.txt", "w");
  if(fp == NULL)
  {
    perror("fopen");
  }
  fclose(fp);
  return 0;
}

如何证明:

使用chdir更改cwd

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

int main()
{
  chdir("/home/suliiik/bite");
  FILE *fp = fopen("log.txt", "w");
  if(fp == NULL)
  {
    perror("fopen");
  }
  fclose(fp);
  return 0;
}

fork的返回值

前面我们没有获取fork的返回值,因此两个进程执行接下来的代码块,没有意义

因此要进行分流

fork
(fork有两个返回值)
如果成功,子进程的pid会返回给父进程,0被返回给子进程;
如果失败,-1被返回给父进程,没有子进程被创建
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());
  pid_t id = fork();
  if(id < 0)
  {
    return 1;
  }
  else if(id == 0)
  {
    //子进程
    while(1)
    {
      printf("I am a child process, pid: %d, ppid: %d\n", getpid(), getppid());
      sleep(1);
    }
  }
  else
  {
    //父进程
    while(1)
    {
      printf("I am a parent process, pid: %d, ppid: %d\n", getpid(), getppid());
      sleep(1);
    }
  }

  return 0;
}

返回值用于做父子分流

父子进程替换

为什么给子进程返回的是0,给父进程返回的是子进程的pid

父进程:子进程=1:n
子进程只需要表明自己是否创建成功即可
给父进程返回标识指定的一个子进程,方便未来控制特定的子进程

fork()是函数,为什么能返回两次?

当一个函数已经准备return了,相当于这个函数的核心功能已经完成了
fork之后,return之前,父子进程已经存在了
也就是说,父子都需要执行return
所以就能返回两次

一个id,为什么能接收两个不同的值?既==0,又>0?

这个问题暂时无法深入解答,要学习虚拟地址空间后才能理解

因为两个进程在物理内存中使用同一块内存(共享)

但是一旦某一方需要对某块内存进行修改,操作系统就会对拷贝一块新的空间(写时拷贝)

父子进程拷贝

因此两个进程的id变量,虽然名称相同,但是实际物理地址不同

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐