歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux進程間通信(IPC)編程實踐(九)System V信號量---封裝一個信號量操作的工具集

Linux進程間通信(IPC)編程實踐(九)System V信號量---封裝一個信號量操作的工具集

日期:2017/3/1 12:20:23   编辑:關於Linux

System信號量集主要API

#include #include
#include int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops);

semget

int semget(key_t key, int nsems, int semflg); /** 示例1: 封裝一個創建一個信號量集函數 該信號量集包含1個信號量;
權限為0666
**/
int sem_create(key_t key)
{
int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
if (semid == -1)
err_exit("sem_create error");
return semid;
} 
/** 示例2: 打開一個信號量集 nsems(信號量數量)可以填0,
semflg(信號量權限)也可以填0, 表示使用默認的權限打開
**/
int sem_open(key_t key)
{
int semid = semget(key, 0, 0);
if (semid == -1)
err_exit("sem_open error");
return semid;
}
int semctl(int semid, int semnum, int cmd, ...);

union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific)*/
}; 

//struct semid_ds : Linux內核為System V信號量維護的數據結構
struct semid_ds
{
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};

/** 示例1: 將信號量集semid中的第一個信號量的值設置成為value(SETVAL) 注意: semun聯合體需要自己給出(從man-page中拷貝出來即可)
**/
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
int sem_setval(int semid, int value)
{
union semun su;
su.val = value;
if (semctl(semid, 0, SETVAL, su) == -1)
err_exit("sem_setval error");
return 0;
} 

/** 示例2: 獲取信號量集中第一個信號所關聯的值(GETVAL)
注意: 此時第四個參數可以不填, 而信號量所關聯的值可以通過semctl的返回值返回(the value of semval.)
**/
int sem_getval(int semid)
{
int value = semctl(semid, 0, GETVAL);
if (value == -1)
err_exit("sem_getval error");
return value;
return 0;
} 

/** 示例3: 刪除一個信號量集(注意是刪除整個集合)
IPC_RMID Immediately remove(立刻刪除) the semaphore set, awakening all processes blocked in semop(2) calls on the set (with an error return and errno set to EIDRM)[然後喚醒所有阻塞在該信號量上的進程]. The argument semnum is ignored[忽略第二個參數].
**/
int sem_delete(int semid)
{
if (semctl(semid, 0, IPC_RMID) == -1)
err_exit("sem_delete error");
return 0;
}


//測試代碼
int main(int argc,char *argv[])
{
int semid = sem_create(0x1234); //創建一個信號量集
sem_setval(semid, 500); //設置值
cout << sem_getval(semid) << endl; //獲取值
sleep(10);
sem_delete(semid); //刪除該集合
} 
/**

示例4: 獲取/設置信號量的權限
注意:一定要設定struct semid_ds結構體, 以指定使用semun的哪個字段
**/
int sem_getmode(int semid)
{
union semun su;

// 注意: 下面這兩行語句一定要設定.
// (告訴內核使用的semun的哪個字段)
struct semid_ds sd;
su.buf = &sd;
//
if (semctl(semid, 0, IPC_STAT, su) == -1)
err_exit("sem_getmode error");
printf("current permissions is: %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode)
{
union semun su;
// 注意: 下面這兩行語句一定要設定.
// (告訴內核使用的semun的哪個字段)
struct semid_ds sd;
su.buf = &sd;
//
sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);

if (semctl(semid, 0, IPC_SET, su) == -1)
err_exit("sem_setmode error");
return 0;
}[cpp] view plaincopy在CODE上查看代碼片派生到我的代碼片
int semop(int semid, struct sembuf *sops, unsigned nsops);[cpp] view plaincopy在CODE上查看代碼片派生到我的代碼片
//sembuf結構體
struct sembuf
{
unsigned short sem_num; /*semaphore number:信號量的編號(從0開始)*/
short sem_op; /* semaphore operation(+1, 0, -1) */
short sem_flg; /* operation flags: 常用取值為SEM_UNDO(解釋見下) */
};

/** 示例: P,V操作封裝
**可以將sembuf的第三個參數設置為IPC_NOWAIT/0, 以查看程序的狀態的變化
**/
int sem_P(int semid)
{
struct sembuf sops = {0, -1, SEM_UNDO};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_P error");
return 0;
}
int sem_V(int semid)
{
struct sembuf sops = {0, +1, SEM_UNDO};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_V error");
return 0;
} 

下面我們封裝一個信號量操作函數工具,將主要的操作封裝起來,可以像命令一樣使用。
/** 信號量綜合運用示例: 編譯完成之後, 直接運行./semtool, 程序將打印該工具的用法;
下面的這些函數調用, 只不過是對上面所封裝函數的稍稍改動, 理解起來並不困難;
**/
//semtool.cpp
#include "Usage.h"

int main(int argc,char *argv[])
{
int opt = getopt(argc, argv, "cdpvs:gfm:");
if (opt == '?')
exit(EXIT_FAILURE);
else if (opt == -1)
{
usage();
exit(EXIT_FAILURE);
}


key_t key = ftok(".", 's');
int semid;
switch (opt)
{
case 'c':
sem_create(key);
break;
case 'd':
semid = sem_open(key);
sem_delete(semid);
break;
case 'p':
semid = sem_open(key);
sem_P(semid);
sem_getval(semid);
break;
case 'v':
semid = sem_open(key);
sem_V(semid);
sem_getval(semid);
break;
case 's':
semid = sem_open(key);
sem_setval(semid, atoi(optarg));
sem_getval(semid);
break;
case 'g':
semid = sem_open(key);
sem_getval(semid);
break;
case 'f':
semid = sem_open(key);
sem_getmode(semid);
break;
case 'm':
semid = sem_open(key);
sem_setmode(semid, argv[2]);
sem_getmode(semid);
break;
default:
break;
}

return 0;
}


//Usage.h
#ifndef USAGE_H_INCLUDED
#define USAGE_H_INCLUDED


#include
#include 

#include 
#include
#include 
#include


#include
#include 
#include
#include 
#include
#include 
#include
#include 
#include
#include 
#include
#include 
#include
#include 
#include
#include 
#include
using namespace std;
inline void err_quit(std::string message);
inline void err_exit(std::string message);

void usage()
{
cerr << "Usage:" << endl;
cerr << "./semtool -c #create" << endl;
cerr << "./semtool -d #delte" << endl;
cerr << "./semtool -p #signal" << endl;
cerr << "./semtool -v #wait" << endl;
cerr << "./semtool -s #set-value" << endl;
cerr << "./semtool -g #get-value" << endl;
cerr << "./semtool -f #print-mode" << endl;
cerr << "./semtool -m #set-mode" << endl; 
}


int sem_create(key_t key)
{
int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
if (semid == -1)
err_exit("sem_create error");
return semid;
}
int sem_open(key_t key)
{
int semid = semget(key, 0, 0);
if (semid == -1)
err_exit("sem_open error");
return semid;
}

union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};

int sem_getmode(int semid)
{
union semun su;

// 注意: 下面這兩行語句一定要設定.
// (告訴內核使用的semun的哪個字段)
struct semid_ds sd;
su.buf = &sd;
//
if (semctl(semid, 0, IPC_STAT, su) == -1)
err_exit("sem_getmode error");
printf("current permissions is: %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode)
{
union semun su;
// 注意: 下面這兩行語句一定要設定.
// (告訴內核使用的semun的哪個字段)
struct semid_ds sd;
su.buf = &sd;
//
sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);

if (semctl(semid, 0, IPC_SET, su) == -1)
err_exit("sem_setmode error");
return 0;
}
int sem_getval(int semid)
{
int value = semctl(semid, 0, GETVAL);
if (value == -1)
err_exit("sem_getval error");
cout << "current value: " << value << endl;
return value;
}
int sem_setval(int semid, int value)
{
union semun su;
su.val = value;
if (semctl(semid, 0, SETVAL, su) == -1)
err_exit("sem_setval error");
return 0;
}


int sem_delete(int semid)
{
if (semctl(semid, 0, IPC_RMID) == -1)
err_exit("sem_delete error");
return 0;
}

// 為了能夠打印信號量的持續變化, 因此sem_flg我們並沒用SEM_UNDO
// 但是我們推薦使用SEM_UNDO
int sem_P(int semid)
{
struct sembuf sops = {0, -1, 0};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_P error");
return 0;
}
int sem_V(int semid)
{
struct sembuf sops = {0, +1, 0};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_V error");
return 0;
}


inline void err_quit(std::string message)
{
std::cerr << message << std::endl;
exit(EXIT_FAILURE);
}
inline void err_exit(std::string message)
{
perror(message.c_str());
exit(EXIT_FAILURE);
}

#endif // USAGE_H_INCLUDED

附:ftok函數

系統建立IPC通訊(如消息隊列、共享內存時)必須指定一個ID值。通常情況下,該id值通過ftok函數得到。

ftok原型如下:

key_t ftok( char * fname, int id )
fname就時你指定的文件名(該文件必須是存在而且可以訪問的),id是子序號,雖然為int,但是只有8個比特被使用(0-255)。
返回值:

當成功執行的時候,一個key_t值將會被返回,否則 -1 被返回。

在我們獲取到key之後,就可以使用該key作為某種方法的進程間通信的key值。

Copyright © Linux教程網 All Rights Reserved