본문 바로가기

카테고리 없음

[리눅스커널] 커널 소스 읽기가 제일 쉬었어요(1) - /proc/cpuinfo

/proc/cpuinfo
/proc/cpuinfo 파일은 CPU 아키텍처 정보를 저장합니다.
 
root:/proc $ cat cpuinfo
Processor       : AArch64 Processor rev 0 (aarch64)
processor       : 0
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x51
CPU architecture: 8
CPU variant     : 0xd
CPU part        : 0x805
CPU revision    : 14
 
processor       : 1
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x51
CPU architecture: 8
CPU variant     : 0xd
CPU part        : 0x805
CPU revision    : 14
 
processor       : 2
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x51
CPU architecture: 8
CPU variant     : 0xd
CPU part        : 0x805
CPU revision    : 14
 
processor       : 3
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x51
CPU architecture: 8
CPU variant     : 0xd
CPU part        : 0x805
CPU revision    : 14
 
processor       : 4
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd0d
CPU revision    : 0
 
processor       : 5
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd0d
CPU revision    : 0
 
processor       : 6
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd0d
CPU revision    : 0
 
processor       : 7
BogoMIPS        : 38.40
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd0d
CPU revision    : 0
 
'proc/cpuinfo' 파일은 proc_cpuinfo_init() 함수에서 03번째 줄을 실행할 때 생성됩니다.
01 static int __init proc_cpuinfo_init(void)
02 {
03    proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
04    return 0;
05 }
06 fs_initcall(proc_cpuinfo_init);
 
proc_cpuinfo_init() 함수 선언부 키워드에 __init이 있으니 부팅 도중 호출된다는 사실을 알 수 있습니다.
 
 
'proc/cpuinfo' 파일 연산
'proc/cpuinfo' 파일로 어떤 연산을 수행하는지 알려면 proc_cpuinfo_operations 전역 변수를 봐야 합니다.
static const struct file_operations proc_cpuinfo_operations = {
.open = cpuinfo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
 
proc 파일 시스템에서 seq_file 인터페이스를 써서 파일 출력을 하므로 open 함수 연산을 수행하는 cpuinfo_open() 함수를 분석할 필요가 있습니다.
 
다음 cpuinfo_open() 함수 코드를 보겠습니다.
01 static int cpuinfo_open(struct inode *inode, struct file *file)
02 {
03 arch_freq_prepare_all();
04 return seq_open(file, &cpuinfo_op);
05 }
 
05번째 줄에서 seq_open() 함수 2번째 인자로 cpuinfo_op seq_file 연산을 지정합니다.
 
struct seq_operations 구조체 타입인 cpuinfo_op 함수 연산은 아키텍처별로 위치가 다릅니다.
 
AArch64 ARMv8 아키텍처의 경우 cpuinfo_op 는 다음 코드에서 확인할 수 있습니다.
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show
};
 
다음은 'cat /proc/cpuinfo' 명령어를 입력했을 때 CPU 아키텍처를 출력하는 c_show() 함수입니다.
01 static int c_show(struct seq_file *m, void *v)
02 {
03 int i, j;
04 bool compat = personality(current->personality) == PER_LINUX32;
05 
06 for_each_online_cpu(i) {
07 struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
08 u32 midr = cpuinfo->reg_midr;
09
10 seq_printf(m, "processor\t: %d\n", i);
11 if (compat)
12 seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
13    MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
14
15 seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
16    loops_per_jiffy / (500000UL/HZ),
17    loops_per_jiffy / (5000UL/HZ) % 100);
 
위 함수 코드와 같이 percpu 타입인 cpu_data 변수로 CPU 정보를 출력한다는 사실을 알 수 있습니다.
 
cpu_data percpu 타입 전역 변수 디버깅해보기 
cpu_data를 크래시 유틸리티(Crash-Utility) 프로그램으로 확인하겠습니다.
crash64_kaslr> p cpu_data
PER-CPU DATA TYPE:
  struct cpuinfo_arm64 cpu_data;
PER-CPU ADDRESSES:
  [0]: ffffffff3f530290
  [1]: ffffffff3f549290
  [2]: ffffffff3f562290
  [3]: ffffffff3f57b290
  [4]: ffffffff3f594290
  [5]: ffffffff3f5ad290
  [6]: ffffffff3f5c6290
  [7]: ffffffff3f5df290
 
출력 결과가 보이듯 percpu 타입 변수란 사실을 알 수 있습니다.
어떤 전역 변수가 percpu 타입 변수면 다음과 같이 떠올릴 필요가 있습니다. 
 
   CPU 갯수만큼 주소 공간이 있다.  
  
cpu_data 변수에서 per-cpu0 정보를 확인하면 다음과 같습니다.
crash64_kaslr> struct cpuinfo_arm64 ffffffff3f530290
struct cpuinfo_arm64 {
  cpu = {
    node_id = 0x0,
    hotpluggable = 0x1,
    dev = {
      parent = 0x0,
      p = 0xffffffff364d3380,
      kobj = {
        name = 0xffffffff36573780 "cpu0",
        entry = {
          next = 0xffffffff3f5492b0,
          prev = 0xffffffff364def68
        },
        parent = 0xfffffffebfec1890,
        kset = 0xfffffffebfeb7280,
        ktype = 0xffffff9689a84170,
        sd = 0xffffffff3654cae8,
        kref = {
          refcount = {
            refs = {
              counter = 0x8
            }
          }
        },
        state_initialized = 0x1,
        state_in_sysfs = 0x1,
        state_add_uevent_sent = 0x1,
        state_remove_uevent_sent = 0x0,
        uevent_suppress = 0x0
      },
      init_name = 0x0,
      type = 0x0,
      mutex = {
        owner = {
          counter = 0x0
        },
        wait_lock = {
          {
            rlock = {
              raw_lock = {
                {
                  val = {
                    counter = 0x0
                  },
                  {
                    locked = 0x0,
                    pending = 0x0
                  },
                  {
                    locked_pending = 0x0,
                    tail = 0x0
                  }
                }
              }
            }
          }
        },
        osq = {
          tail = {
            counter = 0x0
          }
        },
        wait_list = {
          next = 0xffffffff3f530308,
          prev = 0xffffffff3f530308
        }
      },
      bus = 0xffffff9689a84900,
      driver = 0x0,
      platform_data = 0x0,
      driver_data = 0x0,
      links = {
        suppliers = {
          next = 0xffffffff3f530338,
          prev = 0xffffffff3f530338
        },
        consumers = {
          next = 0xffffffff3f530348,
          prev = 0xffffffff3f530348
        },
        status = DL_DEV_NO_DRIVER
      },
      power = {
        power_state = {
          event = 0x0
        },
        can_wakeup = 0x0,
        async_suspend = 0x0,
        in_dpm_list = 0x1,
        is_prepared = 0x0,
        is_suspended = 0x0,
        is_noirq_suspended = 0x0,
        is_late_suspended = 0x0,
        early_init = 0x1,
        direct_complete = 0x0,
        driver_flags = 0x0,
        lock = {
          {
            rlock = {
              raw_lock = {
                {
                  val = {
                    counter = 0x0
                  },
                  {
                    locked = 0x0,
                    pending = 0x0
                  },
                  {
                    locked_pending = 0x0,
                    tail = 0x0
                  }
                }
              }
            }
          }
        },
        entry = {
          next = 0xffffffff3f549370,
          prev = 0xffffffff364df028
        },
        completion = {
          done = 0xffffffff,
          wait = {
            lock = {
              {
                rlock = {
                  raw_lock = {
                    {
                      val = {
                        counter = 0x0
                      },
                      {
                        locked = 0x0,
                        pending = 0x0
                      },
                      {
                        locked_pending = 0x0,
                        tail = 0x0
                      }
                    }
                  }
                }
              }
            },
            head = {
              next = 0xffffffff3f530390,
              prev = 0xffffffff3f530390
            }
          }
        },
        wakeup = 0x0,
        wakeup_path = 0x0,
        syscore = 0x0,
        no_pm_callbacks = 0x1,
        must_resume = 0x0,
        may_skip_resume = 0x0,
        suspend_timer = {
          entry = {
            next = 0x0,
            pprev = 0x0
          },
          expires = 0x0,
          function = 0xffffff9687fe2bd8,
          flags = 0x7
        },
        timer_expires = 0x0,
        work = {
          data = {
            counter = 0xfffffffe0
          },
          entry = {
            next = 0xffffffff3f5303e8,
            prev = 0xffffffff3f5303e8
          },
          func = 0xffffff9687fe2b30
        },
        wait_queue = {
          lock = {
            {
              rlock = {
                raw_lock = {
                  {
                    val = {
                      counter = 0x0
                    },
                    {
                      locked = 0x0,
                      pending = 0x0
                    },
                    {
                      locked_pending = 0x0,
                      tail = 0x0
                    }
                  }
                }
              }
            }
          },
          head = {
            next = 0xffffffff3f530408,
            prev = 0xffffffff3f530408
          }
        },
        wakeirq = 0x0,
        usage_count = {
          counter = 0x0
        },
        child_count = {
          counter = 0x0
        },
        disable_depth = 0x1,
        idle_notification = 0x0,
        request_pending = 0x0,
        deferred_resume = 0x0,
        runtime_auto = 0x1,
        ignore_children = 0x0,
        no_callbacks = 0x0,
        irq_safe = 0x0,
        use_autosuspend = 0x0,
        timer_autosuspends = 0x0,
        memalloc_noio = 0x0,
        links_count = 0x0,
        request = RPM_REQ_NONE,
        runtime_status = RPM_SUSPENDED,
        runtime_error = 0x0,
        autosuspend_delay = 0x0,
        last_busy = 0x0,
        active_jiffies = 0x0,
        suspended_jiffies = 0x0,
        accounting_timestamp = 0xffff8bba,
        subsys_data = 0x0,
        set_latency_tolerance = 0x0,
        qos = 0xffffffff364d2480
      },
      pm_domain = 0x0,
      msi_domain = 0x0,
      pins = 0x0,
      msi_list = {
        next = 0xffffffff3f530490,
        prev = 0xffffffff3f530490
      },
      dma_ops = 0x0,
      dma_mask = 0x0,
      coherent_dma_mask = 0x0,
      bus_dma_mask = 0x0,
      dma_pfn_offset = 0x0,
      dma_parms = 0x0,
      dma_pools = {
        next = 0xffffffff3f5304d0,
        prev = 0xffffffff3f5304d0
      },
      dma_mem = 0x0,
      cma_area = 0x0,
      removed_mem = 0x0,
      archdata = {
        iommu = 0x0,
        dma_coherent = 0x0,
        mapping = 0x0
      },
      of_node = 0xffffffff3f604ea8,
      fwnode = 0x0,
      devt = 0x0,
      id = 0x0,
      devres_lock = {
        {
          rlock = {
            raw_lock = {
              {
                val = {
                  counter = 0x0
                },
                {
                  locked = 0x0,
                  pending = 0x0
                },
                {
                  locked_pending = 0x0,
                  tail = 0x0
                }
              }
            }
          }
        }
      },
      devres_head = {
        next = 0xffffffff3f530530,
        prev = 0xffffffff3f530530
      },
      knode_class = {
        n_klist = 0x0,
        n_node = {
          next = 0x0,
          prev = 0x0
        },
        n_ref = {
          refcount = {
            refs = {
              counter = 0x0
            }
          }
        }
      },
      class = 0x0,
      groups = 0xffffff9689a849c0,
      release = 0xffffff9687fd6778,
      iommu_group = 0x0,
      iommu_fwspec = 0x0,
      offline_disabled = 0x0,
      offline = 0x0,
      of_node_reused = 0x0
    }
  },
  kobj = {
    name = 0xffffff96890b9755 "regs",
    entry = {
      next = 0xffffffff3f530598,
      prev = 0xffffffff3f530598
    },
    parent = 0xffffffff3f5302a8,
    kset = 0x0,
    ktype = 0xffffff96899cdfa8,
    sd = 0xffffffff2e1a9b38,
    kref = {
      refcount = {
        refs = {
          counter = 0x1
        }
      }
    },
    state_initialized = 0x1,
    state_in_sysfs = 0x1,
    state_add_uevent_sent = 0x0,
    state_remove_uevent_sent = 0x0,
    uevent_suppress = 0x0
  },
  reg_ctr = 0x84448004,
  reg_cntfrq = 0x124f800,
  reg_dczid = 0x4,
  reg_midr = 0x51df805e,
  reg_revidr = 0xf,
  reg_id_aa64dfr0 = 0x10305408,
  reg_id_aa64dfr1 = 0x0,
  reg_id_aa64isar0 = 0x100010211120,
  reg_id_aa64isar1 = 0x100001,
  reg_id_aa64mmfr0 = 0x101122,
  reg_id_aa64mmfr1 = 0x10212122,
  reg_id_aa64mmfr2 = 0x1011,
  reg_id_aa64pfr0 = 0x11112222,
  reg_id_aa64pfr1 = 0x0,
  reg_id_aa64zfr0 = 0x0,
  reg_id_dfr0 = 0x4010088,
  reg_id_isar0 = 0x2101110,
  reg_id_isar1 = 0x13112111,
  reg_id_isar2 = 0x21232042,
  reg_id_isar3 = 0x1112131,
  reg_id_isar4 = 0x11142,
  reg_id_isar5 = 0x1011121,
  reg_id_mmfr0 = 0x10201105,
  reg_id_mmfr1 = 0x40000000,
  reg_id_mmfr2 = 0x1260000,
  reg_id_mmfr3 = 0x2122211,
  reg_id_pfr0 = 0x10000131,
  reg_id_pfr1 = 0x10011011,
  reg_mvfr0 = 0x10110222,
  reg_mvfr1 = 0x13211111,
  reg_mvfr2 = 0x43,
  reg_zcr = 0x0
}
 
struct cpuinfo_arm64 구조체 필드 출력 결과를 모두 표시했습니다. 나중에 디버깅을 할 때 필요할지 모르기 때문입니다.
 
눈치가 빠른 분은 위 출력 결과로 Aarch64 아키텍처이란 사실을 유추할 수 있을 것입니다. 
 
   "주소 정보를 16바이트 단위로 출력한다." 
 
이번에 레퍼런스를 위해 ARMv7 아키텍처에서 cpu_data percpu 변수를 확인하겠습니다.
 
crash> p cpu_data
PER-CPU DATA TYPE:
  struct cpuinfo_arm cpu_data;
PER-CPU ADDRESSES:
  [0]: e6b16048
  [1]: e6b29048
  [2]: e6b3c048
  [3]: e6b4f048
  [4]: e6b62048
  [5]: e6b75048
  [6]: e6b88048
  [7]: e6b9b048
 
crash> struct cpuinfo_arm  e6b16048
struct cpuinfo_arm {
  cpu = {
    node_id = 0x0,
    hotpluggable = 0x0,
    dev = {
      parent = 0x0,
      p = 0xe57a6200,
      kobj = {
        name = 0xe57a5680 "cpu0",
        entry = {
          next = 0xe6b2905c,
          prev = 0xe57a720c
        },
        parent = 0xe5e02e08,
        kset = 0xe5df0c80,
        ktype = 0xc17522ec <device_ktype>,
        sd = 0xe57a83c0,
        kref = {
          refcount = {
            counter = 0x3
          }
        },
        state_initialized = 0x1,
        state_in_sysfs = 0x1,
        state_add_uevent_sent = 0x1,
        state_remove_uevent_sent = 0x0,
        uevent_suppress = 0x0
      },
      init_name = 0x0,
      type = 0x0,
      mutex = {
        count = {
          counter = 0x1
        },
        wait_lock = {
          {
            rlock = {
              raw_lock = {
                {
                  slock = 0x20002,
                  tickets = {
                    owner = 0x2,
                    next = 0x2
                  }
                }
              },
              magic = 0xdead4ead,
              owner_cpu = 0xffffffff,
              owner = 0xffffffff
            }
          }
        },
        wait_list = {
          next = 0xe6b16098,
          prev = 0xe6b16098
        },
        owner = 0x0,
        magic = 0xe6b16084
      },
      bus = 0xc17526fc <cpu_subsys>,
      driver = 0x0,
      platform_data = 0x0,
      driver_data = 0x0,
      power = {
        power_state = {
          event = 0x0
        },
        can_wakeup = 0x0,
        async_suspend = 0x0,
        is_prepared = 0x0,
        is_suspended = 0x0,
        is_noirq_suspended = 0x0,
        is_late_suspended = 0x0,
        early_init = 0x1,
        direct_complete = 0x0,
        lock = {
          {
            rlock = {
              raw_lock = {
                {
                  slock = 0x20002,
                  tickets = {
                    owner = 0x2,
                    next = 0x2
                  }
                }
              },
              magic = 0xdead4ead,
              owner_cpu = 0xffffffff,
              owner = 0xffffffff
            }
          }
        },
        entry = {
          next = 0xe6b290d0,
          prev = 0xe57a7280
        },
        completion = {
          done = 0x7fffffff,
          wait = {
            lock = {
              {
                rlock = {
                  raw_lock = {
                    {
                      slock = 0x10001,
                      tickets = {
                        owner = 0x1,
                        next = 0x1
                      }
                    }
                  },
                  magic = 0xdead4ead,
                  owner_cpu = 0xffffffff,
                  owner = 0xffffffff
                }
              }
            },
            task_list = {
              next = 0xe6b160ec,
              prev = 0xe6b160ec
            }
          }
        },
        wakeup = 0x0,
        wakeup_path = 0x0,
        syscore = 0x0,
        no_pm_callbacks = 0x1,
        suspend_timer = {
          entry = {
            next = 0x0,
            pprev = 0x0
          },
          expires = 0x0,
          function = 0xc055b8a0 <pm_suspend_timer_fn>,
          data = 0xe6b16050,
          flags = 0x4
        },
        timer_expires = 0x0,
        work = {
          data = {
            counter = 0xffffffe0
          },
          entry = {
            next = 0xe6b1611c,
            prev = 0xe6b1611c
          },
          func = 0xc055b914 <pm_runtime_work>
        },
        wait_queue = {
          lock = {
            {
              rlock = {
                raw_lock = {
                  {
                    slock = 0x0,
                    tickets = {
                      owner = 0x0,
                      next = 0x0
                    }
                  }
                },
                magic = 0xdead4ead,
                owner_cpu = 0xffffffff,
                owner = 0xffffffff
              }
            }
          },
          task_list = {
            next = 0xe6b16138,
            prev = 0xe6b16138
          }
        },
        wakeirq = 0x0,
        usage_count = {
          counter = 0x0
        },
        child_count = {
          counter = 0x0
        },
        disable_depth = 0x1,
        idle_notification = 0x0,
        request_pending = 0x0,
        deferred_resume = 0x0,
        run_wake = 0x0,
        runtime_auto = 0x1,
        ignore_children = 0x0,
        no_callbacks = 0x0,
        irq_safe = 0x0,
        use_autosuspend = 0x0,
        timer_autosuspends = 0x0,
        memalloc_noio = 0x0,
        request = RPM_REQ_NONE,
        runtime_status = RPM_SUSPENDED,
        runtime_error = 0x0,
        autosuspend_delay = 0x0,
        last_busy = 0x0,
        active_jiffies = 0x0,
        suspended_jiffies = 0x0,
        accounting_timestamp = 0xfffea0e4,
        subsys_data = 0x0,
        set_latency_tolerance = 0x0,
        qos = 0x0
      },
      pm_domain = 0x0,
      pins = 0x0,
      dma_mask = 0x0,
      coherent_dma_mask = 0x0,
      dma_pfn_offset = 0x0,
      dma_parms = 0x0,
      dma_pools = {
        next = 0xe6b16198,
        prev = 0xe6b16198
      },
      dma_mem = 0x0,
      archdata = {
        dma_ops = 0x0,
        dma_coherent = 0x0
      },
      of_node = 0xe6bb34e4,
      fwnode = 0x0,
      devt = 0x0,
      id = 0x0,
      devres_lock = {
        {
          rlock = {
            raw_lock = {
              {
                slock = 0x0,
                tickets = {
                  owner = 0x0,
                  next = 0x0
                }
              }
            },
            magic = 0xdead4ead,
            owner_cpu = 0xffffffff,
            owner = 0xffffffff
          }
        }
      },
      devres_head = {
        next = 0xe6b161cc,
        prev = 0xe6b161cc
      },
      knode_class = {
        n_klist = 0x0,
        n_node = {
          next = 0x0,
          prev = 0x0
        },
        n_ref = {
          refcount = {
            counter = 0x0
          }
        }
      },
      class = 0x0,
      groups = 0xc1f0b0e8 <common_cpu_attr_groups>,
      release = 0xc0552c08 <cpu_device_release>,
      iommu_group = 0x0,
      iommu_fwspec = 0x0,
      offline_disabled = 0x1,
      offline = 0x0
    }
  },
  cpuid = 0x410fd034,
  loops_per_jiffy = 0xd177
}
 
c_show() 함수 콜스택 확인하기 
함수 분석이 끝나면 콜스택을 확인할 필요가 있습니다. 함수 구현부만 보면 시야가 좁아 질 수 있기 때문입니다.
 
c_show() 함수 콭스택은 다음과 같습니다.
<...>-16076 [002] ...1  4396.616120: seq_printf+0x44/0xf0 <-c_show+0x54/0x418
<...>-16076 [002] ...1  4396.616123: <stack trace>
 => ftrace_graph_call+0x0/0xc
 => seq_printf+0x48/0xf0
 => c_show+0x54/0x418
 => seq_read+0x198/0x478
 => proc_reg_read+0x8c/0xe8
 => __vfs_read+0x54/0x158
 => vfs_read+0xa4/0x140
 => __arm64_sys_read+0x5c/0xc0
 => el0_svc_common+0xa0/0x110
 => el0_svc_handler+0x7c/0x98
 => el0_svc+0x8/0xc
 
유저 공간에서 read 시스템 콜을 실행해 proc_reg_read() 함수를 통해 c_show() 함수가 호출됩니다. 
 
정리
이번 시간에 코드를 분석한 내용을 정리해보겠습니다.
 
   " 첫째, /proc/cpuinfo 파일은 어느 함수에서 생성할까?"
 
'proc/cpuinfo' 파일은 proc_cpuinfo_init() 함수에서 03번째 줄을 실행할 때 생성됩니다.
01 static int __init proc_cpuinfo_init(void)
02 {
03    proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
04    return 0;
05 }
06 fs_initcall(proc_cpuinfo_init);
 
   " 둘째, /proc/cpuinfo 파일을 읽을 때 어느 함수가 호출될까?"
 
arch/arm64/kernel/cpuinfo.c 파일 c_show() 함수가 호출됩니다. c_show() 함수 위치는 CPU 아키텍처마다 다릅니다.
 
   " 셋째, /proc/cpuinfo 파일에서 출력하는 자료구조는 무엇인가?"
 
percpu 타입 변수 cpu_data입니다. 
 
 
"혹시 궁금하신 점이 있으면 댓글 달아 주세요. 아는 한 성실히 답변 올려드리겠습니다."