Pthread の排他処理のサンプル
自分用。
- ソース (pthread_mutex.c)
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #define MAX_THREADS 3 void start_threads(void); void thread_main(void *args); void synchronized_printf(const char* s); static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; static pthread_mutexattr_t mattr; void start_threads(){ pthread_t pt[MAX_THREADS]; int i; // スレッドの作成 for(i=0; i<MAX_THREADS; i++){ pthread_create( &(pt[i]), NULL, &thread_main, &i ); } // joinしておかないとメインスレッドが先に終了してしまう。 for(i=0; i<MAX_THREADS; i++){ pthread_join(pt[i], NULL); } return; } void thread_main(void *args){ int i = *((int *)args); int j=0; while(j++<5){ char s[100]; sprintf(s, "[Thread:%d]", i); synchronized_printf(s); sleep( 1 ); } } void synchronized_printf(const char* s){ #ifdef USE_MUTEX pthread_mutex_lock(&mut); //排他処理のためにロックを取得 #endif printf(">>"); printf("%s", s); sleep( rand()%10 ); printf("<<\n"); #ifdef USE_MUTEX pthread_mutex_unlock(&mut); //排他処理のためにロックを解放 #endif } int main(){ pthread_mutex_init(&mut, &mattr); // mutexの初期化 start_threads(); exit(0); }
排他処理なし $ gcc -Wall -g -lpthread pthread_mutex.c -o pthread_mutex 排他処理あり $ gcc -Wall -g -DUSE_MUTEX -lpthread pthread_mutex.c -o pthread_mutex
- 実行結果(排他処理なし)
各スレッドの出力が入り交じっている。
$ ./pthread_mutex >>[Thread:0]>>[Thread:1]>>[Thread:2]<< >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<< << >>[Thread:0]>>[Thread:1]<< << >>[Thread:2]>>[Thread:1]<< << >>[Thread:1]>>[Thread:0]<< << >>[Thread:2]<< << >>[Thread:2]>>[Thread:0]<< <<
- 実行結果(排他処理あり)
各スレッドの出力が綺麗にわかれている。
$ ./pthread_mutex >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<< >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<< >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<< >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<< >>[Thread:0]<< >>[Thread:1]<< >>[Thread:2]<<
C言語で、Gratuitous ARPパケットを送信するサンプル
自分用メモ。
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/param.h> #include <sys/sysctl.h> #include <arpa/inet.h> #include <net/if.h> #include <net/if_arp.h> #include <net/route.h> #include <netinet/in.h> #include <netinet/if_ether.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <netinet/ether.h> void get_ifinfo(char *devname, struct ifreq *ifreq, int flavor) { int iofd; if ((iofd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("ioctl socket creation"); exit(1); } memset(ifreq, '\0', sizeof(*ifreq)); strcpy(ifreq->ifr_name, devname); if (ioctl(iofd, flavor, ifreq) < 0) { perror("ioctl"); exit(1); } return; } void send_gratuitous_arp(const int ifIndex, const char *devname, const char *ipaddress, const char *src_mac_address, const char *dst_mac_address){ int rawfd; struct ether_arp arpbody; struct sockaddr_ll ll_from, ll_to; struct ether_addr src_mac_addr; struct ether_addr dst_mac_addr; ether_aton_r(src_mac_address, &src_mac_addr); ether_aton_r(dst_mac_address, &dst_mac_addr); arpbody.arp_hrd = htons(ARPHRD_ETHER); arpbody.arp_pro = htons(ETH_P_IP); arpbody.arp_hln = 6; arpbody.arp_pln = 4; arpbody.arp_op = htons(ARPOP_REQUEST); memcpy(&(arpbody.arp_sha[0]), src_mac_addr.ether_addr_octet, ETH_ALEN); //arpbody.arp_sha memcpy(&(arpbody.arp_tha[0]), dst_mac_addr.ether_addr_octet, ETH_ALEN); //arpbody.arp_tha *((int *)&(arpbody.arp_spa[0])) = inet_addr(ipaddress); //arpbody.arp_spa *((int *)&(arpbody.arp_tpa[0])) = inet_addr(ipaddress); //arpbody.arp_tpa ll_from.sll_family = AF_PACKET; ll_from.sll_protocol = htons(ETH_P_ARP); ll_from.sll_ifindex = ifIndex; ll_from.sll_hatype = 0; //ARPHRD_ETHER; ll_from.sll_pkttype = 0; //PACKET_BROADCAST; ll_from.sll_halen = ETH_ALEN; //ll_from.sll_addr = NULL; memcpy(ll_from.sll_addr, src_mac_addr.ether_addr_octet, sizeof(src_mac_addr.ether_addr_octet)); ll_to.sll_family = AF_PACKET; ll_to.sll_protocol = htons(ETH_P_ARP); ll_to.sll_ifindex = ifIndex; ll_to.sll_hatype = 0; //ARPHRD_ETHER; ll_to.sll_pkttype = 0; //PACKET_BROADCAST; ll_to.sll_halen = ETH_ALEN; //ll_to.sll_addr = NULL; memcpy(ll_to.sll_addr, dst_mac_addr.ether_addr_octet, sizeof(dst_mac_addr.ether_addr_octet)); if ((rawfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP))) < 0) { perror("raw socket creatin"); exit(1); } if (bind(rawfd, (struct sockaddr *)&ll_from, sizeof(ll_from)) < 0) { perror("bind"); exit(1); } int size=0; if ( (size = sendto(rawfd, (void *)&arpbody, sizeof(struct ether_arp), 0, (struct sockaddr *)&ll_to, sizeof(ll_to))) <= 0) { perror("write"); exit(1); } } int main(int argc, char **argv){ int ifindex = 0; char *devname = "eth0"; char *ipaddr = "10.0.7.1"; char *src_macaddr = "00:12:34:56:78:9a"; char *dst_macaddr = "00:00:00:00:00:00"; struct ifreq ifreq; memset(&ifreq, '\0', sizeof(ifreq)); //ifindex get_ifinfo( devname, &ifreq, SIOCGIFINDEX); ifindex = ifreq.ifr_ifindex; send_gratuitous_arp(ifindex, devname, ipaddr, src_macaddr, dst_macaddr); return 0; }
C言語でNICの情報を取得するサンプル
eth0 の IPアドレス、MACアドレス、ifIndexを取得するサンプル。自分用メモ。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> void get_ifinfo(char *devname, struct ifreq *ifreq, int flavor) { int iofd; if ((iofd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("ioctl socket creation"); exit(1); } memset(ifreq, '\0', sizeof(*ifreq)); strcpy(ifreq->ifr_name, devname); if (ioctl(iofd, flavor, ifreq) < 0) { perror("ioctl"); exit(1); } return; } int main(int argc, char **argv) { struct ifreq ifreq; char *devname = "eth0"; struct sockaddr_in saddr; //IP address get_ifinfo( devname, &ifreq, SIOCGIFADDR); memcpy(&saddr, &(ifreq.ifr_addr), sizeof(saddr)); printf("%s\n", inet_ntoa(saddr.sin_addr)); //MAC address get_ifinfo( devname, &ifreq, SIOCGIFHWADDR); printf("%02x:%02x:%02x:%02x:%02x:%02x\n", (unsigned char)ifreq.ifr_hwaddr.sa_data[0], (unsigned char)ifreq.ifr_hwaddr.sa_data[1], (unsigned char)ifreq.ifr_hwaddr.sa_data[2], (unsigned char)ifreq.ifr_hwaddr.sa_data[3], (unsigned char)ifreq.ifr_hwaddr.sa_data[4], (unsigned char)ifreq.ifr_hwaddr.sa_data[5]); //ifIndex get_ifinfo( devname, &ifreq, SIOCGIFINDEX); printf("%d\n", ifreq.ifr_ifindex); return 0; }
3つのプログラミング言語での syslog の使い方メモ
- C
#include <stdlib.h> #include <syslog.h> int main(int argc, char** argv){ openlog(argv[0], LOG_PID|LOG_PERROR, LOG_LOCAL0); syslog(LOG_INFO, "foo: %d", 123); closelog(); exit(EXIT_SUCCESS); }
#!/usr/bin/perl use strict; use warnings; use Sys::Syslog; sub EXIT_SUCCESS{0} openlog(__FILE__, 'pid,perror', 'local0'); syslog('info', "foo: 123" ); closelog; exit(EXIT_SUCCESS);
#!/usr/bin/python import sys import syslog def main(): syslog.openlog(sys.argv[0], syslog.LOG_PID|syslog.LOG_PERROR, syslog.LOG_LOCAL0) syslog.syslog(syslog.LOG_INFO, 'foo:%s' %(123,)) syslog.closelog() sys.exit(0) if __name__ == "__main__": main()
3つのプログラミング言語での getopt の使い方メモ
C、Perl、Python でのgetoptのサンプル。
とりあえず、help, version, user, password の4つのオプションを指定できるようにした。
- 使い方
% my_getopt --help % my_getopt -h % my_getopt --version % my_getopt -v % my_getopt --user=foo --password=abc % my_getopt -ufoo -pabc % my_getopt --user=foo -pabc
のような感じ。短縮形での指定も可能。
helpを指定した場合には、usage を表示して終了。 version を指定した場合は、バージョン情報を表示して終了。 user とpassword を指定した場合は、その値を表示して終了。
- C
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <getopt.h> static char *user=NULL; static char *password=NULL; void usage(){ printf("Usage: my_getopt --user=<USERNAME> --password=<PASSWORD>\n"); } void version(){ printf("Version 0.0.1\n"); } int main(int argc, char** argv){ int c; while(1){ static struct option long_options[] = { {"user", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {0,0,0,0} }; int option_index = 0; c = getopt_long( argc, argv, "u:p:hv", long_options, &option_index); if( c==-1 ) break; switch(c){ case 'u': user = (char *)malloc( sizeof(char) * (strlen(optarg)+1) ); strncpy( user, optarg, strlen(optarg)+1 ); break; case 'p': password = (char *)malloc( sizeof(char) * (strlen(optarg)+1) ); strncpy( password, optarg, strlen(optarg)+1 ); break; case 'v': version(); exit(EXIT_SUCCESS); case 'h': default: usage(); exit(EXIT_SUCCESS); } } if( !(user && password) ){ usage(); exit(EXIT_FAILURE); } printf ("user: %s\n", user); printf ("password: %s\n", password); exit(EXIT_SUCCESS); }
#!/usr/bin/perl use strict; use warnings; use Getopt::Long; my $user; my $password; my $help; my $version; GetOptions('user=s' => \$user, 'password=s' => \$password, 'help' => \$help, 'version' => \$version, ); if($help){ usage(); exit(0); } if($version){ version(); exit(0); } unless($user && $password){ usage(); exit(-1); } print <<"END_OF_BODY" \$user: $user \$password: $password END_OF_BODY ; sub usage{ print 'perl my_getopt.pl --user=<USERNAME> --password=<PASSWORD>', "\n"; } sub version{ print 'Version: 0.0.1', "\n"; }
#!/usr/bin/python import getopt import sys def usage(): print 'python my_getopt.py --user=<USERNAME> --password=<PASSWORD>' def version(): print 'Version: 0.0.1' def main(): user=None password=None try: opts, args = getopt.getopt(sys.argv[1:], "u:p:hv", ["user=","password=","help", "version"]) except getopt.GetoptError: usage() sys.exit(2) for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-v", "--version"): version() sys.exit() if o in ("-u", "--user"): user = a if o in ("-p", "--password"): password = a if(user==None or password==None): usage() sys.exit(2) print "user:%s" % (user,) print "password:%s" % (password,) sys.exit(0) if __name__ == "__main__": main()
Pythonでprefork型のデーモンを書く
Pythonのお勉強を兼ねて、先日 Cで書いた C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in Seattle をPythonに移植してみた。
条件分岐、ループ、関数定義、ファイル操作、fork、wait、シグナルハンドラ、連想配列、などなど短いプログラムだけど、いろいろな要素が入っているのでなかなか良いサンプルになった。
#!/usr/bin/env python import os import sys import signal import time MAX_CHILDREN=16 PID_FILE='/var/run/my_prefork_daemon.pid' hash_children={} def main(): #デーモン化 daemonize() #プロセスIDを書いておく write_pid() #シグナルハンドラの登録 signal.signal(signal.SIGTERM, kill_all_children ) # 親プロセスのループ # 子プロセスを監視して、子が死んだらすぐに新しい子プロセスを作成する while True: while len( hash_children )>=MAX_CHILDREN: child_pid = os.wait() del( hash_children[child_pid] ) pid = os.fork() if pid == 0: signal.signal( signal.SIGTERM, lambda sig,status:sys.exit(0) ) break else: #ハッシュテーブルに子プロセスのプロセスIDを保存しておく hash_children[pid]=1 time.sleep(0.1) # 子プロセスのループ while True: #ここがメインルーチンになる。 time.sleep(1) def kill_all_children(sig, status): for pid in hash_children.keys(): os.kill( pid, signal.SIGTERM ) os.exit(0) def write_pid(): f = open(PID_FILE, mode='w'); f.write("%d" % os.getpid()) f.close() def daemonize(): os.chdir("/") try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: raise Exception, "%s [%d]" % (e.strerror, e.errno) os.setsid() os.umask(0) sys.stdin = open('/dev/null', 'r') sys.stdout = open('/dev/null', 'w') sys.stderr = open('/dev/null', 'w') if __name__ == "__main__": main()
参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(4): init スクリプト - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(4): init スクリプト
prefork して、シャットダウン可能なデーモン本体ができたので、次はinit スクリプトを用意する。
適当なサンプルは、/etc/init.d/ 以下にたくさんあるのでそれを参考に書いてみた。
デーモン起動時に /var/run/my_prefork_daemon.pid が自動作成されるのを利用して、起動中かどうかを判断し重複起動の防止を行い、シャットダウン時には .pid ファイルを削除している。
- initスクリプト作成
以下のスクリプトをchmod 755して、/etc/init.d/my_prefork_daemon として保存する。 Cで書いた my_prefork_daemon 本体へのパスは要変更。
75,25 という数字は、起動、停止 順序を決める数字(75が起動、25が停止)。前提となるサービスがある場合には、そのサービスの後に起動され、そのサービスの前に停止するように、数字を大きく/小さくする必要がある。
#! /bin/bash # # my_prefork_daemon # # chkconfig: 2345 75 25 # description: foo bar # # Source function library. . /etc/rc.d/init.d/functions prog="my_prefork_daemon" RETVAL=0 start() { echo -n $"Starting $prog: " if [ -e /var/run/$prog.pid ]; then failure echo echo "cannot start $prog: $prog is already running."; return 1 fi daemon /PATH/TO/YOUR/$prog && success RETVAL=$? echo return $RETVAL } stop() { echo -n $"Stopping $prog: " if [ ! -e /var/run/$prog.pid ]; then failure echo echo $"cannot stop $prog: $prog is not running." return 1; fi kill `cat /var/run/$prog.pid` && success RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f /var/run/$prog.pid return $RETVAL } restart() { stop start } case "$1" in start) start ;; stop) stop ;; restart) restart ;; status) if [ -f /var/run/$prog.pid ]; then echo $"$prog is running." RETVAL=0 else echo $"$prog is not running." RETVAL=3 fi ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0
- chkconfig
init スクリプトができたら、chkconfig で自動起動の設定。
サービスに追加 # chkconfig --add my_prefork_daemon 自動起動の設定 # chkconfig my_prefork_daemon on サービス一覧表示 # chkconfig --list 〜前略〜 crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off my_prefork_daemon 0:off 1:off 2:on 3:on 4:on 5:on 6:off network 0:off 1:off 2:on 3:on 4:on 5:on 6:off postfix 0:off 1:off 2:on 3:on 4:on 5:on 6:off sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 〜後略〜
- service コマンドで起動、停止のテスト
# service my_prefork_daemon start Starting my_prefork_daemon: [ OK ] # service my_prefork_daemon restart Stopping my_prefork_daemon: [ OK ] Starting my_prefork_daemon: [ OK ] # service my_prefork_daemon stop Stopping my_prefork_daemon: [ OK ] # service my_prefork_daemon status my_prefork_daemon is not running.
これでマシン起動時に自動で my_prefork_daemon が起動され、マシン停止時には自動でシャットダウンされる。
参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in Seattle