C言語でclearコマンドを書きたい

■要求定義
– 消さずに画面を綺麗にする

■検討
– windowsでsystem(“cls”); で消せると書いてある。嫌な予感がするが、bionicでも動く?

#include 
#include 

int main(){

	for(int idx=0; idx<10; idx++){
    	printf("%d\n", idx);
  	}

	system("clear");
		
	return 0;
}

0
1
2
3
4
5
6
7
8
9
sh: 1: cls: not found

やっぱり。DOS/Windowsコマンドらしく、linuxには無いとのこと。

int main(){

	for(int idx=0; idx<10; idx++){
    	printf("%d\n", idx);
  	}

	system("clear");
		
	return 0;
}

これなら動くけど意味ないね。

エスケープシーケンシーとはターミナルを制御する制御文字
printf("\033[2J") //画面クリア
などがある。

system("\033[2J");


う〜ん、clearコマンドとはちょっと違うな。

C言語でcpコマンドを書きたい

■要求定義
– ファイルやフォルダをコピーする
– cp (移動元ファイル名/フォルダ名) (移動先ファイル名/フォルダ名) 

#include <stdio.h>
#include <stdlib.h> //汎用ライブラリ
#include <string.h>

int main(){

	FILE *fpSrc, *fpDes, *fp;
	char ss[256], ch[50];

	fpSrc = fopen("test.txt", "r");
	fpDes = fopen("copy.txt", "w");

	while(fgets(&ss, 256, fpSrc) != NULL){
		printf("%s", ss);
		fwrite(ss, 1, 8, fpDes);
	}

	fclose(fpSrc);
	fclose(fpDes);

	printf("コピーしました。\n");
		
	return 0;
}

$ ./dev
asdf
1234コピーしました。

■考察
あれ、コピー先のデータはバイナリデータで入ってるな。
調べてみると、fwriteはバイナリデータの書き込みとのこと。
fwriteではなく、fputs()に変更します。

int main(){

	FILE *fpSrc, *fpDes;
	char ss[256];

	fpSrc = fopen("test.txt", "r");
	fpDes = fopen("copy.txt", "w");

	while(fgets(&ss, 256, fpSrc) != NULL){
		printf("%s", ss);
		fputs(ss, fpDes);
	}

	fclose(fpSrc);
	fclose(fpDes);

	printf("コピーしました。\n");
		
	return 0;
}

コピーできました。
バイナリとは2進数データのこと。

C言語でmvコマンドを書きたい

■要求定義
– ファイルを移動させる

■ステップ1
– 標準ライブラリ関数であるrename関数を使う

#include <stdio.h>

int main(){

	rename("./test.c", "./hoge/test.c");
	printf("移動しました。\n");
		
	return 0;
}

$ ./dev
移動しました。

移動できた事を確認。

■ステップ2
– ユーザ入力で$ mv ${FileName} ${MoveDir}となるので、コマンドラインからの入力を元に移動させたい
– 文字列の分割は”strtok”を使う。ポインタでファイル名s1, 異動先s2を指定する。

#include <stdio.h>
#include <string.h>

int main(){

	char ss[256] = "./test.c ./hoge/test.c";
	char *s1, *s2;
	s1 = strtok(ss, " ");
	s2 = strtok(NULL, " ");
	rename(s1, s2);	
	printf("移動しました。\n");
		
	return 0;
}

■ステップ3
– gets()でコマンドラインから入力した値を取得して移動させたい

int main(){

	// char ss[256] = "./test.c ./hoge/test.c";
	char ss[256];
	char *s1, *s2;
	
	gets(ss);
	s1 = strtok(ss, " ");
	s2 = strtok(NULL, " ");
	rename(s1, s2);	
	printf("移動しました。\n");
		
	return 0;
}

$ ./dev
./test.c ./hoge/test.c
移動しました。

ほう、面白い。コマンドラインからの入力は、strtokで分割して処理すれば良いですな。

C言語でcdコマンドを書きたい

■要求定義
– ユーザ名の入力に合わせてカレントディレクトリを移動する

■検討事項
ディレクトリ一覧を取得し、ユーザ入力と一致したら移動か
+ chdirでディレクトリを変更

#include <stdio.h>
#include <string.h> // 文字列操作
#include <unistd.h> // standard symbolic constants and types

int main(){

	char pathname[512];
	memset(pathname, '\0', 512); // memsetはバイトメモリブロックのセット

	char path[64] = "./hoge";
	chdir(path);
	getcwd(pathname, 512); // getcwdはカレントディレクトリ
	printf("%s\n",pathname);
		
	return 0;
}

$ pwd
/home/vagrant/dev
$ ./dev
/home/vagrant/dev/hoge

cだと、chdir(path)でディレクトリを変更できる

C言語でlsコマンドを書きたい

■要求定義
– まずはファイル名一覧表示
– オプションは後から考える

■検討した事
標準関数でファイル名一覧の取得関数はないらしい

#include <stdio.h>
#include <dirent.h> // format of directory entries

int main(){
	DIR *dir;
	struct dirent *dp; // 構造体
	char path[64] = "./";

	dir=opendir(path); // opendirはディレクトリオープン、 dirent.hが必要

	for(dp=readdir(dir);dp!=NULL;dp=readdir(dir)){ // readdirはディレクトリ読み込み
		printf("%s\n",dp->d_name);
	}
		
	return 0;
}

$ ./dev
.
dev
hello.c
..

forループでdp=readdir(dir);dp!=NULL;dp=readdir(dir)の書き方がよくわからんな。

C言語でログイン処理を書きたい

さて、C言語の基礎を写経したので、ここから少しずつC言語を使って緩くOSを学んでいきたい
まずmac, windowsにもあるログイン処理から

■要求定義
– ユーザ名に一致したパスワードを入力し、一致していたらログイン
– 不一致の場合は、パスワードを再入力してくださいと表示

#include <stdio.h>
#define User "hpscript"
#define Password "asdf"

int main(){
	char s[5];
	
	while(1){
		printf("%sさん、パスワードを入力してください。\n", User);
			gets(s);
		if(strcmp(s, Password)==0){
			printf("ログイン成功しました。\n");
			break;
		} else {
			printf("%sさん、パスワードを入力してください。\n", s);
		}
	}
	
	return 0;
}

$ ./dev
hpscriptさん、パスワードを入力してください。
aaaa
aaaaさん、パスワードを入力してください。
hpscriptさん、パスワードを入力してください。
asdf
ログイン成功しました。

文字列の比較は、strcmpを使用する
if(s == Password){} としても、trueにならない。
while(1)で無限ループにして、一致した場合はbreakでループから抜ける。

なるほど、型の宣言するところとか、色々頭使うなー

記憶装置の原理

PCが記憶装置を制御している
– データ授受の為の情報通路: データ線、データバスと呼ばれる
– 読み出し・書込みの制御信号: R/Wを通告する
– 記憶装置のどの位置を選択するかのアドレス線

基本記憶素子をメモリセルという
主記憶装置の構造は
1. アドレス選択部
2. 制御部
3. 記憶部: 一つのアドレスに1バイト

RAM
L SRAM: 高速アクセス、フリップフロップ、バイポーラ型、MOS型のトランジスタが用いられている
L DRAM: コンデンサに電荷を蓄える
フラッシュメモリ
 大容量で書き換え可能回数の多い
キャッシュメモリ
 主記憶装置とCPUの間に小容量の高速メモリを入れる

プロセッサ基本機能

コンピュータの構成要素は、入力部、記憶部、演算部、出力部、制御部からなる。
一般的に演算部と制御部をまとめたものをプロセッサとして実現する
機械語がプロセッサを実行する

### 汎用プロセッサ
CPU
1チップで実現されたプロセッサをマイクロプロセッサという
例えば、通信制御プロセッサやデータベースプロセッサなど

### 専用コンピュータ
用途に合わせてプロセッサ自体も設計する

画像処理用プロセッサ…GPU
デジタル信号処理用プロセッサ…DSP
LSI設計用プロセッサ…CAD

命令実行部の構成回路

Program Counter
– 実行するプログラムの主記憶装置の先頭アドレス
– 命令はInstruction Registerで保持される
レジスタ群
– MARにデータアドレスが入れられ、それによって主記憶装置上のデータから読み出す
– データはレジスタに入れられる
Decoder
– 命令を解読する
– 選択された信号を制御部に渡す
アドレス計算回路
– 演算実行対象となるデータの読み出しのアドレスを計算して求める場合がある


演算実行部はデータに対する演算加工を実行する
クロック回路はクロック信号が発生するごとに動作を一つ一つ進めていく

-構成回路間の接続
各回路間はデータや制御信号の受け渡しのために接続される
直接接続と、バス線を設け、これに各回路を接続するバス回路方式がある

### アーキテクチャー
CPUアーキテクチャ
– 命令セット
命令語の構成、形式、種別、数の表現方法、オペランドの指定方法などを定める
– システム制御
割り込み方式、システムを管理するためのアクセス権利設定などを定める
– プロセッサ内部構造
回路構成および高速化のための制御方式の設計

メモリアーキテクチャ
主記憶がもつ物理的容量を超えた領域を含めたアドレスをどのように指定制御するか

入出力アーキテクチャ
入出力動作に関する命令又は司令をどのような方式で行うか

### 命令
命令の形式には命令部と一つ以上のオペランド部から構成される
– 命令部 + R1 + R2 : Add , 1, 2
– 命令部 + R + メモリアドレス : Add, 1, 2のメモリアドレス
– 命令部 + データ + メモリアドレス
– 命令部 + メモリアドレス1 + メモリアドレス2 : Add, 1のメモリアドレス, 2のメモリアドレス
– 命令部のみ
直接アドレス指定、間接アドレス指定(ポインタ)があある
命令、アドレスモード(AM)、オペランド1、オペランド2

### 動作の流れ
プロセッサの処理は予め用意されたプログラムによって処理される
プログラムカウンタ(PC)からプログラムを命令を読み出し、命令レジスタ(IR)に入れられる
命令デコーダ(DEC)で解読を行い、アドレス計算回路でレジスタもしくはデータに命令を出す
レジスタから演算回路を使用し、演算回路で演算を実行する
演算結果はレジスタもしくはメモリに返される

linuxの物理プロセッサーの表示
vagrant@ubuntu-bionic:~/dev$ grep physical.id /proc/cpuinfo | sort -u | wc -l
1

-命令の概要
データ転送、演算、ストリング操作、ビット操作、プログラム転送、プロセッサコントロール

なるほど、メモリ上のプログラムから、プロセッサを通じて演算処理が行われていることは理解しました。

C言語のファイル入出力

ファイル処理の手順は、ファールオープン->ファイルの読み書き->ファイルクローズとなる。

int main(void){

	FILE *fp;
	int ch;

	fp = fopen("myfile1.txt", "r");
	if (fp == NULL) return 1;

	ch = fgetc(fp);
	printf("ch=%c\n", ch);

	fclose(fp);

	return 0;
}

$ touch myfile1.txt
$ sudo vi myfile1.txt
$ ./dev
ch=a

ファイルが正常にオープンされると、ポインタを返す
FILE *fi, *foが良く使われる
入出力データの処理を効率的に行う為、ストリームという概念を使用する(stdin, stdout, stderr)
fp = fopen(“myfile1.txt”, “r”); と記述する

### fopen関数のオープンモード
r: read
w: write
a: apend
rb: binary fileのread
wb: binary fileのwrite
ab: binary fileのappend
※バイナリファイルとは自動変換を行わないこと

### ファイル入出力関数
fputs(), fgetc(), fputs(), fgets(), fprintf(), fscanf()

int main(void){

	FILE *fp;
	char ss[256];

	if((fp=fopen("myfile1.txt", "r")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	while (fgets(ss, 256, fp) != NULL){
		printf("%s", ss);
	}

	fclose(fp);

	return 0;
}

### ファイル出力

int main(void){

	FILE *fp;
	int dt = 1234;

	if((fp=fopen("myfile1.txt", "w")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	fputs("char output\n", fp);
	fputc('A', fp);
	fputc('\n', fp);
	fprintf(fp, "dt=%d\n", dt);

	fclose(fp);

	return 0;
}

ファイルコピー

int main(void){

	FILE *fin, *fout;
	char infile[80], outfile[80], ss[256];

	printf("inputfile="); gets(infile);
	printf("outputfile="); gets(outfile);

	if((fin=fopen(infile, "r")) == NULL){
		printf("cannot open input file.\n");
		return 1;
	}
	if((fin=fopen(outfile, "w")) == NULL){
		printf("cannot open output file.\n");
		return 1;
	}

	while(fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

### コマンドラインから引数を渡す

int main(int argc, char *argv[]){

	FILE *fin, *fout;
	char ss[256];

	if(argc != 3){
		printf("引数の数が異なります\n");
		return 1;
	}

	if((fin=fopen(argv[1], "r")) == NULL){
		printf("cannot open inputfile\n");
		return 1;
	}
	if((fin=fopen(argv[2], "w")) == NULL){
		printf("cannot open outputfile\n");
		return 1;
	}

	while (fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

argv[0]はコマンド名そのもの。
argcは全ての引数、argv[]は対応する引数

int main(int argc, char *argv[]){

	if(argc != 3) return 1;
	printf("argc =%d\n", argc);
	printf("argv[0] =%s\n", argv[0]);
	printf("argv[1] =%s\n", argv[1]);
	printf("argv[2] =%s\n", argv[2]);

	return 0;
}