プロセス、スレッドとは

前にも、こんな記事を書いた気がするがまた調べたのでメモ。

1.1 プロセスとスレッドの違い
   ・プロセス
    ・ファイルディスクリプタの情報
    ・プログラムの実行状況
    ・CPUの割り振り時間
     とかプログラムを実行する上で必要な情報の固まり

   ・スレッド
    ・プロセス内の処理の単位


1.2 以下のように捉えると分かりやすい
   ・(シングルスレッド)普通のプログラムを実行すると
     (1)プログラムの実行に必要な情報の固まり(プロセス)が生成される
     (2)1つの処理単位である(スレッド)が実行される(必要な情報はプロセスで管理されている)

   ・上記のように考えれば、マルチスレッドは簡単に捉えられる。
     ・処理単位を1つ増やすだけである
       →あくまでも、処理単位を増やすだけであり、必要な情報の複製等は行っていない
       →1つ目のスレッド(プログラム実行時初めに生成される)とメモリの情報は共有される



2.1 2種類の並列処理、マルチプロセスとマルチスレッドの違い
   ・マルチプロセス
     →親プロセスがプログラムの実行に必要な情報をコピーし、1つのスレッドを生成
       →メモリ空間がコピーされる(親プロセスとメモリ空間が別)

   ・マルチスレッド
     →1つのプロセスの中で処理単位(スレッド)を1つ増やすだけ
       →メモリ空間などは、1個目の処理単位(スレッド)と共有される



2.2 スレッドの種類について
  ・ユーザスレッド
     →ユーザ空間で実装されたスレッド機構
     →ユーザがスレッド切り替えをする必要がある
     →1プロセスで動いているスレッドは、必ず1つ(マルチコアの恩恵受けられない)

   ・カーネルスレッド
     →カーネル空間で実装されたスレッド機構
     →カーネルがスレッドの切り替えをする
     →1プロセスで動いているスレッドは複数ある
     →プロセス管理の観点から見たら、プロセスとほぼ同じ。コンテキストスイッチ等のプロセス並みのオーバヘッドが生じる

2.3 そこでライトウェイトプロセスが出てくる
   ・カーネルスレッドはライトウェイトプロセスというユーザスレッド群を管理するプロセスを管理
(1)ユーザスレッド群のスケージュリングは、ユーザ空間で行う
(2)ライトウェイトプロセスは、スケジューリング結果より実行すべきユーザスレッドを実行する
     (3)オーバヘッドは、カーネルスレッド>ライトウェイトプロセス>ユーザスレッド
   
   ・カーネルスレッドとライトウェイトプロセスは、1対1または、1対0で結びつけられる
→1対0のカーネルスレッドは、カーネルの処理を行うカーネルスレッドである
   ・ライトウェイトプロセス+カーネルスレッド=ネイティブスレッド



3.1 まとめ
   ・プロセスをプログラムの実行単位と捉えるよりは、プロセスは1つ以上のスレッドを持っており、スレッドが実行単位と考える方がよい
   ・ユーザスレッドは、1プロセス内の複数のスレッドを同時(複数コア上)に動かすことはできない
   ・カーネルスレッドは、1プロセス内の複数のスレッドを同時(複数コア上)に動かすことができる
      →ただし、カーネルが管理するスレッド数には、上限がある
      →コンテキストスイッチ等のプロセス並みのオーバヘッドが生じる
   ・ライトウェイトプロセス
      →カーネルスレッドは、1つのLWPを管理し、LWPがユーザスレッド群を管理する
      →ライトウェイトプロセスを使うと、複数コア上でスレッドを走らせることが可能
        →Cライブラリがスレッド構築と同時に、CWPの生成とユーザランドで動くスケジューラ生成してくれる
        →LWPの生成、ユーザスレッドと結びつける等のLWPに関する操作システムコールがある
        →ライトウェイトプロセス+カーネルスレッド=ネイティブスレッドと呼ぶこともある

カメラが少し好きになった大学生がしたこと

最近は、あまりパソコンに触れてなくて、シェルスクリプトとか文法忘れててもうガン萎えですわ。

そんな感じですがタイトルの通り。最近カメラ買いまして、それに伴って写真の管理をどうしようと考えたときにこんなのがあればいいなと

 ・友達に撮った写真(以前に行った海外旅行とか)を見せたい
   →しかし、普段持ち歩くのには容量制限がある。iphoneの容量も限られてるし。。。
   →faceboookに載せたくない写真も、あるし。。。。

って思ったので、家のパソコンでに写真を貯めて、出先から家のPCにアクセスするようにしようと思いその環境を構築しようと思います。大雑把に必要な手順はこんな感じ。

1、写真を管理するパソコン(ubuntu)でnfsサーバーの設定
2、apacheの設定(サムネイルの自動生成モジュールのインストール[smalllight,imagemagick])
3、グローバルIPアドレスの取得
4、写真をいい感じに見れるWEBアプリの作成

とりあえず3まで、行けば最低限の物はできるかと。そして今は4に取り組もうというところです。

2のsmalllightのインストールで若干ハマりましたが「http://qiita.com/tototoshi/items/472f2b09e48ae9717a2d」ここを見てすんなり行けました。
とりあえず、smallightはgithubから最新のものをcloneしてくるってのがミソですね。

今回の本題は、こんなことではなく。3番の「グローバルIPアドレスの取得」なんですよ。

 固定回線を引いてる人は、既にグローバルIPアドレスが割り当てられていて、ポートフォワーディングの設定だけで外部(携帯とか公衆のWIFIとか)からアクセスできるようにすることができるかもしれませんが、自分の家はWimaxなんですよ。
 Wimaxはプロバイダから割り当てられるIPアドレスはプライベートであるため外部から自分のPCにアクセスするのは不可能なんです。。。。
 
 んで、グローバルIPをもらうために固定回線を引こうとマンションに入ってる光の業者に電話すると個人向けにはプライベートIPしか発行してないとのこと。。。。

これをマズい・・・

そこでしばらく検索してると、「グローバルIP発行します!どのプロバイダにも対応しています!」の文字が!
 正直、どうやったらプロバイダに管理されている自分のPCに見ず知らずの業者が割り込んでグローバルIPアドレスを発行できるのか!?と不思議だったけど仕組みを聞いてなるほどっ。

 どうやら、vpnを利用してグローバルIPを発行しているみたいなんです!(頭いいなぁ)確かにVPNを利用すればグローバルIPアドレスを割り当ててもらうことは可能ですね〜。ただ、全部のパケットがVPNサーバーを通して送受信されるので速度は少し落ちそう。ちなみにここ(http://www.interlink.or.jp/

んで、ubuntuグローバルIPを割り当てるためにubutuからvpnを接続します。がしかし、ここで問題がwimaxってこともあってたびたび回線状況が悪くなるとvpnが切断されるんですね。しかも再接続は手動でしかできないんですよ。。。。

これだと、外部からまったく安定して自宅pcにアクセスできない。。。
ってことで、常時接続するために、cronで定期的にvpnの状況をチェックして切断されてたらvpn接続するようにしようと思ったんですよ。

まず、そのためにpptpの接続をコマンドに置き換えました。

#sudo pptpsetup create
#sudo pppd call updetach
#sudo route add default ppp0

GUIでやると、ルーティングテーブルの変更までやってくれるんだけどCUIの場合は自分でやる必要があります!これをやらないと外部からパケットは受け取れるのにこちらから送るパケットはvpnサーバーを通さないのでちゃんと通信できません。

とりあえず作ったスクリプトは適当にこんな感じ

pppd_procee=$(ps aux | grep pppd | wc -l)
if test $pppd_prcee -lt 3 ; then
sudo pppd call updetach
sudo route add default ppp0
fi

pppdのプロセス数で判別する感じです。あんまりイケてないけどこれでもちゃんといけるはず。
これを1分間隔でcronで実行します!

そのときに重要なのは、ttyがないとか言われるので/etc/sudoersに
Default !requiretty
ALL=(ALL) NOPASSWS:ALL
の2つのの行を追加しておくことです。

sudoは、実行にttyを必要とするので、必要ないように変更しパスワードも聞かれないように変更します。
こうすることで、上記のスクリプトがcronで実行できます!

とりあえずこれで、外部から家のパソコンの写真が見れるようになりました!

webアプリに関しては、これからちょいちょいやっていきます!

C言語で文字列を扱うということ〜ポインタと配列〜

文字列配列と文字列ポインタの関係

文字列について、
文字列には、配列で文字列を扱う方法とポインタで文字列を扱う方法があります。
まずは、それぞれのアドレスを調べてみるとそれぞれの関係が見えてきます。

char *test_pointer = "test pointer"
char test_array[] = "test array"

printf("test_pointer:%p¥n",test_pointer);
printf("&test_pointer:%p¥n",&test_pointer);
printf("&test_pointer[0]:%p¥n", &test_pointer[0]);

printf("test_array:%p¥n",test_array);
printf("&test_array:%p¥n",&test_array);
printf("&test_array[0]:%p¥n",&test_array[0]);

出力は以下の通りになります。

test_pointer:31c8a7
&test_pointer: 5dc343
&test_pointer[0]:31c8a7

test_array:12fed8
&test_array:12fed8
&test_array[0]:12fed8

そうそう、この結果に正直おどろきました。
&test_arrayとtest_arrayが同じアドレスを指すのです。
test_arrayとtest_array[0]が同じアドレスを指すことを知っている人は多いですが、上記を知っている人は
そう多くないと思います(自分のその一人です^^;)
でもこれではっきりしたことがあります。
配列はあくまでも配列であり、ポインタはあくまでもポインタです。
上記の関係を図で分かりやすくしてみます


f:id:ukinau:20131026170620p:plain


test_arrayという変数は配列を保持する変数なので、変数の中には「test_array」が入ってます。
test_pointerという変数は、アドレスを保持する変数なので、変数の中には「31c8a7」つまりアドレスが入ってます。

ただ、混乱させてしまう原因は、
配列は、
配列の先頭アドレス == 変数のアドレス
ということが成り立つことが原因です。
復習ですが、
test_arrayは、添字なしで使うと配列先頭アドレスを示します。
&test_arrayは、test_array変数のアドレスを示します

なので、

test_array:12fed8
&test_array:12fed8
&test_array[0]:12fed8

という出力が得られたのです。

ポインタの方は、図を見れば分かると思うので解説しません。

Macでudpソケットプログラミング中のエラー "sendto: message too long"

大学の実験で、UDP上でTCPっぽい振る舞いを作れという課題があって、その課題を解いているうちにぶち当たったエラー

int error = sendto(sock, sendpkt, (int)sizeof(sendpkt), 0, distination_addr, sizeof(distination_addr));
if(error < 1){
  perror("sendto");
  exit(1);
}

こんなコードがあったとして、sendpktの大きさが9000ぐらいになると

”message too long”
こんなエラーを受け取って、データを送れない。

バッファの容量が足りてないのかな?と思って、

printf("SO_SNDBUF:%d",SO_SNDBUF);

”SO_SNDBUF:4097”

ソケットの送信バッファは4097!?これバッファ漏れ発生するんじゃね?

バッファ漏れはしませんでした。ってことは、本当はバッファサイズもっとデカいのか?

試します。

int val;
int len = sizeof(val);
getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &val, &len);
printf("%d¥n",val);

”9216”

おっと、getsockoptで設定されているバッファサイズは9216バイトらしい。

SO_SNDBUFに必ず正しい値が入ってるとは限らないんですね。。。。

この流れだと、setsockoptでbufferサイズを変更しても変わらないので、カーネルのパラメータでそれっぽいのを探します。

$sysctl -a | grep udp
..
net.inet.udp.maxdgram: 9216
..

この、net.inet.udp.maxdgramがカーネルが設定しているUDPデータグラムの最大サイズなのでこの値を、変更してあげるとソケットのバッファサイズの最大サイズもあげることができるようになります。

$sysctl -w net.inet.udp.maxdgram=<大きい値>

これで、"sendto: message too long" のエラーは解消されます。

まとめ

"sendto: message too long"エラーの原因は、カーネルのパラメータのnet.inet.udp.maxdgramが送りたいバイト数より小さかったのでsocketでエラーが起きました。

forkを使った並列処理〜変数の値〜

前回は、forkを使った並列処理の概要を少し話しました。
今回は、親プロセスと子プロセスの変数の値についてまとめます。

変数の値

変数は全てコピーされます。
しかし、まったく別の空間なので子プロセスが親プロセスの変数にアクセスすることはできません。
これは、浅いコピーだから、値コピーだからという話ではありません
以下のようなプログラムを書いてみました

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(){
  int i = 1;
  int *iaddr = &i;
  int pid;
 
  if((pid = fork())<0){
    perror("call fork()");
    exit(1)
  }
  //this is child process
  if(pid == 0){
    *(iaddr)=0;
    printf(" my name is child process ¥n  &i  is %x, value is %d¥n",iaddr,*(iaddr));
}//this is mother process
  else{
    *(iaddr)=10;
    printf(" my name is mother process ¥n  &i is %x, value is %d¥n",iaddr,*(iaddr));
  }
 
}

これを実行すると、以下のような出力が得られます。

                • -

my name is child process
&i is 0x5c7d2
i is 0
my name is mother process
&i is 0x5c7d2
i is 10

                  • -

アドレスの値が一緒なので、親プロセスの変数にアドレス経由ならアクセスできるかな?
って思った人もいるかもしれませんが。
あくまでも、別空間です!以下にイメージを示します
f:id:ukinau:20130930001046j:plain

ということで、変数はコピーされますがそれは親プロセスのモノとはまったく別物です。

では、どうやって親プロセスと子プロセスはデータをやりとりするのか?
詳しくは、次の記事で紹介しますがpipeやsocketを使います。

forkを使った並列処理〜概要〜

forkを使って並列処理をやってみようって話題が研究室内で出たので、ちょっと勉強してみた。
rubyとかjavaでスレッドは使ったことあったけど、プロセスの複製を行う並列処理は初めてなのでいろいろ勉強になってる。

ちょっとまとめてみた。

概要

forkは、新しいプロセスを生成するシステムコールです。
もう少し正確にいうと、呼び出し元のプロセスを複製して子プロセスを生成します。

まぁ、これだけなんですがちょっとこれでは、分かりづらいので実際にプログラムを書いてみます。


#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(){
 
  int pid;
 
  if((pid = fork())<0){
    perror("call fork()");
    exit(1)
  }
  //this is child process
  if(pid == 0){
    printf(" my name is child process ¥n pid variable is %d ¥n pid is %d ¥n mother process is %d",pid,getpid(),getppid());
  }//this is mother process
  else{
    printf(" my name is mother process ¥n , pid variable is  %d ¥n pid is %d",pid,getpid());
  }
 
}

上記プログラムを実行すると以下のような出力が得られます。

                                • -

my name is child process
pid variable is 0
pid is 574 #=> 実行する度に変わります
mother process is 698 #=> 実行する度に変わります

my name is mother process
pid variable is 574
pid is 698

                                • -


fork()を実行するとプログラムは2つに分かれます。イメージは以下の通り

f:id:ukinau:20130929125622j:plain

実はこれが結構分かりづらい。
親プロセスの方のpidには、子プロセスのプロセスIDが入り、子プロセスの方には必ず0が入ります。
上記のサンプルを見てもらっても分かるんですが、pid周りは、以下のような感じになります

f:id:ukinau:20130929125732p:plain


pid(forkの返り値)は自分のホスト内で一意の値が割り当てられるという表記をよく見るのですが、
これは、親側から見た話です。

子供からみたpid(forkの返り値)は常に0です。

Ruby 言語内 DSL

ってなわけで、おもむろに「初めてのRuby」を手に取ったわけですが。

大分忘れてます^^;

解釈に少し時間がかかったので、メモしときます。P175

今までは、ただ何も考えず使うだけだったRakeとか便利な言語内DSL

その中身がどんなんかなんて考えたこともなかったです。

言語内DSLでは、どうやらコンテキストのすり替えを行うことで、目的の処理を実行しているようです。

dsl_1,dsl_2みたいな命令があったとしたら、DSL言語内部は多分こんな感じ

class TestDsl

  def dsl &prc
    self.instance_eval(&prc) 
  end

  def dsl_1
    p "dsl_1"
  end
  
  def dsl_2
    p "dsl_2" 
  end

end

このように書いとくと、以下のようにして呼べる

TestDsl.new.dsl do
 dsl_1
 dsl_2
end

#これを実行すると
# => "dsl_1"
# => "dsl_2" って出力される

分かってしまえば、当たり前のことなんだけど、ちゃんと理解できてなかった。
上記コードでは、dsl_1,dsl_2の呼び出しのレシーバは、TestDslのインスタンスになっている

通常ブロック呼び出しのところの文のレシーバは、mainになるんだけどここでは、

「self.instance_eval(&prc) 」

の部分で、評価コンテキストをTestDslにすり替えて実行している。


勉強足りないな〜。
とりあえず、これからの予定として
・gemの作り方を調べる
・rakeのソースリーディング
この二つをやっていこうかな。