semaphoreセマフォとは

semaphoreセマフォとは、プロセス間でリソースの排他制御をするものですが、Raspbian Linuxとpepopifaceで実装した

pepopifaceとはRaspberry piの拡張基板PiFace を制御するものです。ダウンロードはhttp://osdn.jp/projects/pepolinux/downloads/63750/pepopiface-0.6.tar.gz/ から

セマフォを使うには初期化が必要です

  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
    void *__pad;
  };
  union semun my_semun;

共有ロックするには各プロセスで共通のkeyが必要です。

以下の関数使用例では、予め用意した任意のパス+'S'の8ビットでkeyを作成

#define PIFACE_SEMA "/var/run/pepopiface.semaphore"

key = ftok(PIFACE_SEMA, 'S');

パスを元に作成したkeyで一個のセマフォIDをgetします。0666はファイルのパーミッションと同じ

mysemun_id = semget(key, 1, 0666 | IPC_CREAT);

getしたセマフォIDの0番目のセマフォに'1'を設定して初期化します

このvalの値を各プロセスが減算、加算してロック、アンロックをします

my_semun.val = 1;

semctl(mysemun_id, 0, SETVAL, my_semun);

これでセマフォが操作出来るようになりました

リソースを使う時は優先ロックを掛けます

sem_op=-1を設定してsemop関数を呼びます

semop関数は、valをマイナスして結果が負にならければこの関数の呼び出しが終了して、元のルーチンに戻ります

もし、結果が負になるとsemop関数の呼び出しでロックされて、優先ロックしたプロセスのアンロック操作待ちになります

/* The following are defined in the "<sys/sem.h>" */
//  struct sembuf {
//    unsigned short  sem_num;        /* semaphore index in array */
//    short           sem_op;         /* semaphore operation */
//    short           sem_flg;        /* operation flags */
//  }; 
void mysem_lock(int sid){
  struct sembuf mysemop[1];
  mysemop[0].sem_num = 0;
  mysemop[0].sem_op = LOCK;
  mysemop[0].sem_flg = SEM_UNDO;
  if(semop(sid, mysemop, 1) == -1){
    perror("semop: semop lock-1 failed");
    exit(1);
  }

次の関数は共有リソースの使用が終わりロックを開放する為に、sem_op=1を設定してsemop関数を呼びます

valをプラスして結果が負にならければこの関数の呼び出しが終了して元のルーチンに戻ります

void mysem_unlock(int sid){
  struct sembuf mysemop[1];
  mysemop[0].sem_num = 0;
  mysemop[0].sem_op = UNLOCK;
  mysemop[0].sem_flg = SEM_UNDO;
  if(semop(sid, mysemop, 1) == -1){
    perror("semop: semop unlock failed");
    exit(1);
  }

pepopiface.cでは、最初オプションなしで起動すると、セマフォ用ファイルを作成し、そのパスkeyを元にセマフォIDを取得、初期化します

次に通常の起動ではオプションは必ずあるので、作成されたセマフォIDを元に共有リソースを使う前にロック、使い終わったら開放します

セマフォ用のファイル作成、IDの取得、初期化を何時のタイミングで行うか暫く悩みましたがオプションあり、なしで判定して初期化処理をしました

こうする事でもしロックされたままの場合でも、オプションなしで起動すれば新しいセマフォIDを作成させ初期化する事ができます

root@pepopiface# make

gcc -Wall -L/usr/local/lib/ -lpiface-1.0 -o pepopiface pepopiface.c

root@pepopiface# ./pepopiface

** Welcome to pepopiface Version-0.4 Copyright Yamauchi.Isamu compiled:Dec 26 2014 **

usage:pepopiface port:0-8 0|1 [timer:0-65535ms]

root@pepopiface# ls /var/run/pepopiface.semaphore

/var/run/pepopiface.semaphore

root@pepopiface# ipcs -s


key semid owner perms nsems

0x530eb51d 894107648 root 666 1

セマフォ用のファイルとセマフォのキーが作成される

通常は必ずオプションがある、この場合はポート0へ『0』を書き込み、ライト後のアフタリードの結果は『0』

root@pepopiface-0.4# time ./pepopiface 0 0

0

real 0m0.014s

user 0m0.000s

sys 0m0.000s

root@pepopiface#

DEBUGを有効にしてmake・・・pepopiface.debug

セマフォの値valをプリント、ロック10秒待ちアンロックします

root@pepopiface-0.4# time ./pepopiface.debug 1 0

semop_lock:val=0

sem_unlock:val=1

semop_lock:val=0

sem_unlock:val=1

semop_lock:val=0

sem_unlock:val=1

0semop_lock:val=0

sem_unlock:val=1

semop_lock:val=0

lock & wait 10000 milliseconds

sem_unlock:val=0

real 0m10.042s

user 0m0.010s

sys 0m0.000s

root@pepopiface#

DEBUGコード有りコマンドの10秒待ちの間に、別shellでDEBUG無効のコードを起動

DEBUG無効のコードは8秒程終了待ちをしている事が分かる

root@pepopiface-0.4# time ./pepopiface 0 0

0

real 0m8.277s

user 0m0.000s

sys 0m0.000s

root@pepopiface# ipcs -s


key semid owner perms nsems

0x530eb51d 894107648 root 666 1

セマフォキーを削除

root@pepopiface# ipcrm -S 0x530eb51d

root@pepopiface# ipcs -s


key semid owner perms nsems

セマフォキーが削除された、次にセマフォー用のキーファイルを削除

root@pepopiface# rm /var/run/pepopiface.semaphore

rm: remove regular empty file /var/run/pepopiface.semaphore'? y

この状態でポート0の読み取りを行うと、エラーを表示してオプションなしを促します

root@~# pepopiface 0

ftok: failed: No such file or directory

** Please start first time is with no options **

/*
Copyright Isamu.Yamauchi 2013-2017. update 2015.8.21
This program uses the libpiface. thanks.
pepopiface.c is controls for Raspberry pi PiFace Digital I/O Expansion Board

o 2013.07.20 ver0.1, 1st release.
o 2013.9.5 ver0.2 , to match the input state of piface, modified to highlight the port data.
o 2014.12.21 ver0.3, add an exclusive processing.
o 2014.12.26 ver0.4, read & write command return value bug fixes.
o 2015.1.11 ver0.5 , add without writing timer option to continuous processing to read.
o 2015.8.21 Ver0.6 , Change the upper limit of 60 seconds timer to 5 minutes.
*/

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/sem.h>
/* <sys/ipc.h> is include in sem.h
#include <sys/ipc.h>
*/
#include <libpiface-1.0/pfio.h>
#define DEBUG
#undef  DEBUG /* Comment out the case when debugging */
#define READ 'R'
#define WRITE 'W'
#define VER "0.6"
#define DAY "compiled:"__DATE__
#define LOCK -1
#define UNLOCK 1
#define PIFACE_SEMA "/var/run/pepopiface.semaphore"
#ifdef DEBUG
#define DEBUG_WAIT 10000 /* debug wait timer, ms */
#endif

int get_myval(int sid) {
  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
    void *__pad;
  };
  union semun my_semun;
  uint16_t d_result = semctl(sid, 0, GETVAL, my_semun);
  if (d_result == -1) {
    perror("semctl: GETVAL failed");
    exit(1);
  }
#ifdef DEBUG
  printf("%s%d\n","val=",d_result);
#endif
  return(d_result);
}

int get_sempid(int sid) {
  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
    void *__pad;
  };
  union semun my_semun;
  pid_t sem_pid;
  sem_pid = semctl(sid, 0, GETPID, my_semun);
  if (sem_pid == -1) {
    perror("semctl: GETPID failed");
    exit(1);
  }
  return(sem_pid);
}

void usage() {
  union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
    void *__pad;
  };
  union semun my_semun;
  FILE *fdsem;
  uint16_t d_result;
  int mysemun_id;
  key_t key;
#ifdef DEBUG
  pid_t my_pid, sem_pid;
  my_pid = getpid();
#endif
  fprintf(stderr,"\r\n** Welcome to pepopiface Version-%s Copyright Yamauchi.Isamu %s **",VER,DAY);
  fprintf(stderr,"\n\rusage:pepopiface port:0-8 [0|1] [timer:0-300000ms]\n\r");
  fdsem = fopen(PIFACE_SEMA,"r");
  if (fdsem != NULL) {
    if ((key = ftok(PIFACE_SEMA, 'S')) == -1) {
      perror("ftok: failed");
      exit(1);
    }
/* Creating of the semaphore */
  mysemun_id = semget(key, 1, 0666 | IPC_CREAT);
  if (mysemun_id == -1) {
    perror("semget: semget get failed");
    exit(1);
  }
#ifdef DEBUG
  sem_pid = get_sempid(mysemun_id);
  fprintf(stderr,"\r\nmy_pid:%d, sem_pid:%d\r\n",my_pid, sem_pid);
#endif
/* remove of the semaphore */
  my_semun.val = 1;
  if (semctl(mysemun_id , 0, IPC_RMID, my_semun) == -1) {
    perror("semctl: semaphore remove failed");
    exit(1);
  }
/* semaphore of the file delete */
  unlink(PIFACE_SEMA);
}
  fdsem = fopen(PIFACE_SEMA,"r");
  if (fdsem == NULL) {
/* File creation of semaphore */
    fdsem = fopen(PIFACE_SEMA,"w");
    if (fdsem == NULL) {
      perror("fopen: failed");
      exit(1);
    }
#ifdef DEBUG
    fprintf(stderr,"\r\n** %s file creation succeed of semaphore! **\r\n",PIFACE_SEMA);
#endif
    fclose(fdsem);
  }
  if ((key = ftok(PIFACE_SEMA, 'S')) == -1) {
    perror("ftok: failed");
    exit(1);
  }
/* Creating of the semaphore */
  mysemun_id = semget(key, 1, 0666 | IPC_CREAT);
  if (mysemun_id == -1) {
    perror("semget: semget Initialization failed");
    exit(1);
  }
  d_result = get_myval(mysemun_id);
  if (d_result == 0) {
/* Initialization of the semaphore */
    my_semun.val = 1;
    if (semctl(mysemun_id, 0, SETVAL, my_semun) == -1) {
      perror("semctl: Initialization failed");
      exit(1);
    }
#ifdef DEBUG
    fprintf(stderr,"\r\n** Initialization succeed of semaphore! **\r\n");
#endif
  }
}

/* The following are defined in the "<sys/sem.h>" */
//  struct sembuf {
//    unsigned short  sem_num;        /* semaphore index in array */
//    short           sem_op;         /* semaphore operation */
//    short           sem_flg;        /* operation flags */
//  };

void mysem_lock(int sid){
  struct sembuf mysemop[1];
  mysemop[0].sem_num = 0;
  mysemop[0].sem_op = LOCK;
  mysemop[0].sem_flg = SEM_UNDO;
  if(semop(sid, mysemop, 1) == -1){
    perror("semop: semop lock-1 failed");
    exit(1);
  }
#ifdef DEBUG
  printf("semop_lock:");get_myval(sid);
#endif
}

void mysem_unlock(int sid){
  struct sembuf mysemop[1];
  mysemop[0].sem_num = 0;
  mysemop[0].sem_op = UNLOCK;
  mysemop[0].sem_flg = SEM_UNDO;
  if(semop(sid, mysemop, 1) == -1){
    perror("semop: semop unlock failed");
    exit(1);
  }
#ifdef DEBUG
  printf("sem_unlock:");get_myval(sid);
#endif
}

int msleep(int ms) {
  if ( ms > 0 ) {
    struct timeval timeout;
    timeout.tv_sec = ms / 1000;
    timeout.tv_usec = (ms % 1000) * 1000;
    if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
      perror("msleep");
      return 1;
    }
  }
  return 0;
}

int main(int argc, char *argv[]) {
  int port = 0;
  int data = 0;
  int invert_data = 0;
  int wait_time = 1;
  int port_timer = 0;
  char rw_flag = READ;
  int i = 0x0000;
  int j = 0x0000;
  uint8_t s_result = 0x00;
  uint16_t d_result = 0x0000;
  int mysem_id = 0;
  key_t key;
  char patterns[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
  if ( argc > 4 || argc < 2  ) {
    usage();
    exit(1);
  }
  else {
    port = atoi(argv[1]); 
    if ( port > 8 || port < 0 ) {
      usage();
      exit(1);
    }
  }
  
  if ( argc == 3 ||  argc == 4) {
    data = atoi(argv[2]);
    if ( data != 0 && data != 1 ) {
      usage();
      exit(1);
    }
    else {
      rw_flag = WRITE;
    }
  }
  if ( argc == 4 ) {
    port_timer = atoi(argv[3]);
    if ( port_timer < 0 || port_timer > 300000 ) {
      usage();
      exit(1);
    }
    rw_flag = WRITE;
  }
  if ((key = ftok(PIFACE_SEMA, 'S')) == -1) {
    perror("ftok: failed");
    fprintf(stderr,"\r\n** Please start first time is with no options **\r\n");
    exit(1);
  }  
/* Creating of the semaphore */
  mysem_id = semget(key, 1, 0666 | IPC_CREAT);
  if (mysem_id == -1) {
    perror("semget: semget Initialization failed");
    exit(1);
  }
  mysem_lock(mysem_id);
  if (pfio_init() < 0) exit(-1);
  mysem_unlock(mysem_id);
  if ( rw_flag == WRITE ) {
/* port write */
    mysem_lock(mysem_id);
    pfio_digital_write(port, data);
    if ( port_timer > 0 ) {
      mysem_unlock(mysem_id);
      if ( data == 0 ) {
        invert_data = 1;
      }
      else {
        invert_data = 0;
      }
      msleep(port_timer);
      mysem_lock(mysem_id);
      pfio_digital_write(port, invert_data);
    }
    msleep(wait_time);
    s_result = pfio_read_output() & patterns[port];
    mysem_unlock(mysem_id);
    s_result = s_result >> port;
    fprintf(stderr,"%1x",s_result);
  }
  if ( rw_flag == READ ) {
/* port read */
    mysem_lock(mysem_id);
    if ( port == 8 ) {
      i = pfio_read_input() & 0xffff;
      j = pfio_read_output() << 8;
      j = j & 0xffff;
      i = i ^ 0xffff;
      i = i & 0x00ff;
      d_result = j | i;
      fprintf(stderr,"%04x",d_result);
    }
    else {
      s_result = (pfio_read_input() & patterns[port]);
      s_result = s_result >> port;
      s_result = s_result ^ 0x01;
      fprintf(stderr,"%1x",s_result);
    }
    mysem_unlock(mysem_id);
  }
  msleep(wait_time);
  mysem_lock(mysem_id);
  pfio_deinit();
  mysem_unlock(mysem_id);
#ifdef DEBUG
  mysem_lock(mysem_id);
  fprintf(stderr,"lock & wait %d milliseconds\n",DEBUG_WAIT);
  msleep(DEBUG_WAIT);
  mysem_unlock(mysem_id);
#endif
  exit(0);
}