派遣で働くエンジニアのスキルアップを応援するサイト

PRODUCED BY RECRUIT

【基本情報技術者試験のトッカカリ】 「プログラム」はどのように実行されるのか

 

ハードウェア編、最後の今回はプログラム実行の仕組みです。プログラムはどのように実行されるのか、プログラマであっても説明できる人は少ないでしょう。IPAの基本情報技術者試験でも出題されますが、馴染みない言葉が並んで、皆さんが難しいと感じやすいポイントでもあります。

今回は、わかりやすくするため、端折った部分もありますが、この内容を知っておくと、「コンピューターがどんなものなのか」を理解する助けになるでしょう。

今日のニャゴロウ講座
プログラムはどのように実行されるのか?

▼これまでの【基本情報技術者試験のトッカカリ】

プログラムが実行される仕組み

プログラムは、普段、内部ストレージにありますが、多くのパソコンでは、プログラム実行時には、メモリ(主記憶装置)にコピーされ、CPUで処理されます。

では、メモリとCPUでは、何が起こっているのでしょうか。

プログラムは、小さな命令の集団に分かれて処理されます。その命令は、次々とメモリからCPUに読み込まれて処理されるのですが、その時に使われるのが、CPUの中にある「プログラムカウンタ(命令アドレスレジスタ)」「命令レジスタ」「番地レジスタ(アドレスレジスタ)」の3つのレジスタ(記憶装置)です。簡単にいうと、これらのレジスタになんらかの情報が記録されて、CPUがゴニョゴニョするのです。順番に一つずつ見ていきましょう。

①呼び出し表の「プログラムカウンタ(命令アドレスレジスタ)」

CPUは、命令をいっぺんに全部処理することはできないので、メモリで処理を待っている命令たちに「1番の方お入りくださ~い」と順番に次々呼んでくるのですが、「次は何番を呼ぶ」とカウントし、その順番の命令がメモリのどこにあるのかを記録しているのが、プログラムカウンタです。

CPUは、待っている命令たちの中身を知っているわけではなく、単純に、プログラムカウンタのカウントしている番号順にCPUの中へご案内します。

「お呼び出し表」は、大した仕事ではないと思うことなかれ!命令は長いものもあれば、短いものもあるので、そのあたりも良い案配でやってくれるのが、プログラムカウンタです。お呼び出しして、処理が進んだら、次の番号を呼び出します。

②命令を保持する「命令レジスタ」

命令レジスタは、命令アドレスレジスタ(プログラムカウンタ)と名前が似ていますが、別のものです。紛らわしいですね!誤字じゃないんですよ。

CPUは、プログラムカウンタに従って、メモリ(主記憶装置)から、命令を自分のところにコピーしてきますが、命令をコピーして、置く先が「命令レジスタ」です。命令には、「命令部」と「オペランド部(アドレス部)」という部分があります。CPUは、命令部を解読器(デコーダ)に投げて解読してもらったり、オペランド部の情報をアドレスレジスタに投げたりします。

③データの場所を記録する「番地レジスタ(アドレスレジスタ)」

番地レジスタ(アドレスレジスタ)は、命令において参照データがどこにあるかを保持するものです。命令のうち、「オペランド部(アドレス部)」には、命令を実行する対象となるデータ(参照するデータ)がメモリのどこにあるのか書いてあります。そのオペランド部の情報を番地レジスタは保持し、CPUは、それを見てデータの場所にアクセスします。

あまり聞き慣れない言葉が多くて、マゴマゴする方もいらっしゃるかもしれませんね。全体の流れをザックリ整理しておきましょう。

まず、動くのはCPU君、そしてレジスタは記憶装置です。レジスタは、CPU君の中にあります。メモリが家の中のタンスだとすれば、レジスタは、CPU君のポケットです。

プログラムは、一旦、メモリに保存されますが、そこから命令として、もっと小さい単位で実行されます。

そのとき、メモリ(タンス)から、命令レジスタ(自分のポケット)に命令がコピーされ、処理が始まるのです。命令は、プログラムカウンタ(別のポケット)にある情報に従って、順に呼び出されて、コピーされます。コピーされたら、命令のうち、「命令部」の情報は、解読器(デコーダ)に投げられて解読されます。「オペランド部(アドレス部)」の情報は、番地レジスタに保持され、CPUが処理をする時に、そこに書いてある情報を元に、データにアクセスするのです。

命令の中身「命令部とオペランド部(アドレス部)」

チラっと書きましたが、命令の中は「命令部」と「オペランド部(アドレス部)」で構成されています。

命令部とは、CPUが「なにをすべきか」という命令が書かれています。といっても、文章で書かれているというよりは、対応する命令が数字で表記されているのです。

オペランド部は、「なにかをする対象」が書かれています。そのまま格納されていることもありますし、対象(データ)がメモリに置いてある場合は、メモリ内の住所(番地)が書いてあります。そもそも、オペランドとは、「被演算子」の訳で、「演算される側」という意味です。住所が書いてあることが多いので、アドレス部とも言います。

つまり、命令部には、「足し算して!」などの命令が書かれていて、オペランド部に「タンスの3段目にある数字を」のように対象が書かれているのです。

アドレス(番地)の指定の仕方

オペランド部(アドレス部)では、演算の対象となるデータの場所を指定していますが、それにはいくつかの方法があります。最後にそれを紹介しておきましょう。

①命令のオペランド部に格納する(即値アドレス指定)

命令のオペランド部にそのまま格納する方法です。「即値アドレス指定」と言います。直接格納しているのですから、アドレスの指定をしていないのでは?と思いますが、大人の事情で、このような名前になっています。予想が付くと思いますが、もちろん小さいものに限る話です。

②メモリ内の格納場所を記載する(直接アドレス指定)

データが大きい場合は、メモリ内(タンスの中)に置いておき、命令のオペランド部には、そのデータの置き場所(アドレス)だけを記載します。「タンスの何段目」みたいな感じです。「直接アドレス指定」と言います。即値アドレスと直接アドレスとでは、名前が紛らわしいですね。「即」はピッタリとくっつくことなので、そのように覚えておきましょう。

③メモリ内の格納場所を書いてある場所を記載する(間接アドレス指定)

ちょっとここからややこしい話になってきます。直接アドレス指定では、アドレスをそのまま書きましたが、「間接アドレス指定」という方法もあるのです。

間接アドレス指定は、まずは「タンスの5段目を見ろ」と書いてあるので、5段目にアクセスすると「データの場所は12段目にあるよ」と書いてある方法です。何のために、そんな意地悪をするのかと思うかもしれませんが、昔の汎用機コンピューターでは、この方法を取らないと、大きなデータを扱えなかったのです。現在では、どちらかというと、参照先の切り替えに便利なので使われるのではないでしょうか。

④ややこしい感じで記載する(相対アドレス指定/インデックスアドレス指定/ベースアドレス指定)

この他に、「相対アドレス指定」や、「インデックスアドレス指定」、「ベースアドレス指定」があります。これらは、仕組みとしては同じです。

相対アドレス指定を例に取ると、オペランド部に書いてある値に、プログラムカウンタ(命令アドレスレジスタ)にある値を足すことで、アドレスを指定するものです。

これも「意味がわからない!!」と思われそうですが、要は、指定するアドレスを変動させたい時に便利なのです。

インデックスアドレス指定は、プログラムカウンタではなく、インデックスレジスタを使う方法で、ベースアドレス指定の場合なら、変わりにベースレジスタを使います。要は、「オペランド部に、どこかのレジスタの値を足す」ってことです。

ニャゴロウ先生のまとめ

さて、ハードウェアのうち、プログラムの実行に関わる部分について、3回で説明してきましたが、なんとなく、掴めたでしょうか。

1回目でもお話しましたが、ハードウェアは、最近、実際に触ったことのある人が減ってきているので、想像しづらいジャンルになっています。CPUの中身の話はともかく、CPU自体や、ストレージ、メモリなどは、画像検索すれば、すぐに実物の写真を見ることができるのですから、ぜひ、見てみてくださいね。

実感をもって理解していくことが、試験合格へのトッカカリになるかもしれません。
【筆者】小笠原 種高さん(ニャゴロウ先生)
技術ライター、イラストレーター。システム開発のかたわら、雑誌や書籍などで、データベースやサーバー、マネジメントについて執筆。図を多く用いた易しい解説に定評がある。主な著書に『なぜ?がわかるデータベース』(翔泳社)、『図解即戦力 Amazon Web Serviceのしくみと技術がこれ1冊でしっかりわかる教科書』『ゼロからわかるLinuxサーバー超入門 Ubuntu対応版』(技術評論社)、『仕組みと使い方がわかる Docker&Kubernetesのきほんのきほん』(マイナビ出版)がある。

※本記事に記載されている会社名、製品名はそれぞれ各社の商標および登録商標です。

'); $backTopNav.appendTo($('.no-entry .entry-content')); } ////////////////////////////////////////////////////////// /* /* カテゴリ **/ function setLocalNav(){ // 右カラム:イベントレポートのナビ生成 var $reportNav = $('#js-report-nav'); if ( $reportNav.length > 0 ) { var reportCatParentIndex = 2; var reportCats = itseCategory[reportCatParentIndex][1]; $.each(reportCats,function(index, val) { $('
  • '+itseCategoryText[reportCatParentIndex][1][index]+'
  • ').appendTo($reportNav); }); } // 右カラム:コラム・連載のナビ生成 var $columnNav = $('#js-column-nav'); if ( $columnNav.length > 0 ) { var columnCatParentIndex = 1; var columnCats = itseCategory[columnCatParentIndex][1]; $.each(columnCats,function(index, val) { $('
  • '+itseCategoryText[columnCatParentIndex][1][index]+'
  • ').appendTo($columnNav); }); } } function setClassCategories(){ $('.categories a').each(function(){ if(!$(this).hasClass('.urllist-category-link')){ $(this).addClass('urllist-category-link category-'+$(this).text()); } }); } setLocalNav(); setClassCategories(); ////////////////////////////////////////////////////////// /* /* カレンダー **/ var eventData; var Calendar = function (elm) { this.$elm = elm; } Calendar.prototype.init = function () { this.$calendar = null; this.calendarDate = {}; this.settings = { year: 0, month: 0, weekValue: ['æ—¥', '月', '火', 'æ°´', '木', '金', '土'], areaClass: 'calendar', activeDateClass: 'active-date', prevNavClass: 'prev', nextNavClass: 'next', sunClass: 'sun', satClass: 'sat', todayClass: 'today', emptyClass: '', headerFormat: 'yå¹´m月', emptyValue: ' ' } var date = this.getDate(); this.calendarData = { year: date.year, month: date.month, date: date }; this.today = date.days; this.currentMonth = date.month; this.currentYear = date.year; this.setup(); } Calendar.prototype.getDate = function () { var date = new Date(); var toYear = date.getFullYear(); var toMonth = date.getMonth() + 1; var toDays = date.getDate(); var result = {}; result = { year: toYear, month: toMonth, days: toDays }; return result; }; Calendar.prototype.getPrevDate = function () { var result = { year: this.calendarData.year, month: this.calendarData.month }; if (result.month === 1) { result.year--; result.month = 12; } else { result.month--; } return result; }; Calendar.prototype.getNextDate = function () { var result = { year: this.calendarData.year, month: this.calendarData.month }; if (result.month === 12) { result.year++; result.month = 1; } else { result.month++; } return result; }; Calendar.prototype.zeroFormat = function (v, n) { var vl = String(v).length; if (n > vl) { return (new Array((n - vl) + 1).join(0)) + v; } else { return v; } }; Calendar.prototype.weekClass = function (weekNumber, targetDate) { var date = this.getDate(); var classArr = []; var classStr = ''; if (weekNumber === 0) { classArr.push(this.settings.sunClass); } else if (weekNumber === 6) { classArr.push(this.settings.satClass); } if (targetDate && targetDate === '' + date.year + this.zeroFormat(date.month, 2) + this.zeroFormat(date.days, 2)) { classArr.push(this.settings.todayClass); } if (classArr.length > 0) { classStr = ' class="' + classArr.join(' ') + '"'; } return classStr; }; Calendar.prototype.createBaseHTML = function () { var htmlStr = ''; htmlStr += '\n
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; htmlStr += '
    \n'; this.$calendar = $(htmlStr); this.$calendar.addClass(this.settings.areaClass); this.$calendar.find('.calendar-month').addClass(this.settings.activeDateClass); this.$calendar.find('.calendar-prev').addClass(this.settings.prevNavClass); this.$calendar.find('.calendar-next').addClass(this.settings.nextNavClass); }; Calendar.prototype.createBody = function () { var self = this; var year = this.calendarData.year; var month = this.calendarData.month; var lastdays = new Date(year, month, 0); var forDate = new Date(year, month - 1, 1); var emptyCell = '
  • ' + this.settings.emptyValue + '
  • \n'; var rowCount = 1; var htmlStr = ''; var i; var j; var len; htmlStr = '\n'; htmlStr += '\n'; htmlStr += '\n'; this.$calendar.find('.calendar-body').html(htmlStr); }; Calendar.prototype.setHeader = function () { var headerText = this.settings.headerFormat.replace('y', this.calendarData.year).replace('m', this.calendarData.month); this.$calendar.find('.calendar-month').html(headerText); }; Calendar.prototype.setup = function () { var self = this; this.createBaseHTML(); this.setHeader(); this.createBody(); // ヘッダー内、左右のリンクをクリックしたときの処理 this.$calendar.find('.js-change-month a').on('click', function () { var mode = $(this).attr('href').replace(/^#/, ''); if (mode === 'prev') { self.prevMonth(); } else if (mode === 'next') { self.nextMonth(); } return false; }); this.$elm.append(this.$calendar); }; Calendar.prototype.changeCalendar = function (year, month) { var date = this.getPrevDate(); if (year && month && String(year).match(/^[0-9]{4}$/) && String(month).match(/^[0-1]?[0-9]$/)) { this.calendarData.year = Number(year); this.calendarData.month = Number(month); this.setHeader(); this.createBody(); } }; Calendar.prototype.prevMonth = function () { var date = this.getPrevDate(); this.changeCalendar(date.year, date.month); }; Calendar.prototype.nextMonth = function () { var date = this.getNextDate(); this.changeCalendar(date.year, date.month); }; ////////////////////////////////////////////////////////// var getEventData = function () { var d = $.Deferred(); // dataを作る var data = {}; eventSchedule.forEach(function(v, i){ var date = new Date(v.date); var event = { category: v.category, year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate(), url: "http://www.itstaffing.jp" + v.url, title: v.title, }; data[i+1] = event; }); // ここまで d.resolve(data); return d.promise(); } if ($("#calendar").length > 0) { var getData = getEventData(); getData.then(function (data) { eventData = data; //console.log(eventData); var calendar = new Calendar($("#calendar")); calendar.init(); }); } //}; }); }();