linux 进程间通讯--信号量
PV的定义 P就是请求资源,V就是释放资源
P(sv) 如果sv大于0,减小sv。如果sv为0,挂起这个进程的执行。
V(sv) 如果有进程被挂起等待sv,使其恢复执行。如果没有进行被挂起等待sv,增加sv。
P表示通过的意思,V表示释放的意思
System V机制
一.头文件
#include <sys/sem.h>
二.结构体
struct sembuf {unsigned short sem_num; /* semaphore index in array *///处理的信号量个数short sem_op; /* semaphore operation *///要执行的操作类型short sem_flg; /* operation flags *///操作标识};
union semun { int val; struct semid_ds *buf; unsigned short *array;} arg;
三.API函数
1.获取key_t值
key_t ftok(const char *path, int id);
2.获取设置信号量
int semget(key_t key, int nsems, int semflg);
参数:信号量集键值,信号量格式,信号量标志
返回值:成功返回semid,,错误返回-1,
错误号errno:
EACCES 权限过低
EEXIST:id存在,但((semflg &IPC_CREAT) &&(semflg &IPC_EXCL))=0
EINVAL:nsems参数不对
ENOENT:id不存在且semflg &IPC_CREAT=0
ENOSPC:信号量数超过系统限制
当key为IPC_PRIVATE或者当不存在key指定的id时且semflg &IPC_CREAT=0 会创建信号量
3.信号量控制
int semctl(int semid, int semnum, int cmd, ...);
参数:信号量集键值,信号量遍号,命令,可选参数(union semun)
返回值:成功返回整数,是吧返回-1
命令:
GETVAL:返回信号量集中的一个单个的信号量的值
SETVAL:设置信号量集中的一个单独的信号量的值
GETPID:返回最后一个执行semop操作的进程的PID
GETNCNT:用于读取信号量集中的所有信号量的值
GETZCNT:返回这在等待完全空闲的资源的进程数目
GETALL:用于读取信号量集中的所有信号量的值
SETVAL:设置信号量集中的一个单独的信号量的值
IPC_STAT:读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中
IPC_SET:设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数
IPC_RMID:将信号量集从内存中删除
4.信号量操作
int semop(int semid, struct sembuf *sops, size_t nsops);
参数:信号量集键值,sembuf结构体,操作标志
返回值:成功返回0,失败返回-1
sop->sem_ops的说明:
值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;
值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;
值为0,则操作将暂时阻塞,直到信号的值变为0
四.例子
1.
#include <sys/types.h>#include <stdio.h>#include <sys/ipc.h>#include <sys/sem.h>#include <sys/stat.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <pwd.h>#include <fcntl.h>#include <limits.h>int main(int argc,char **argv){key_t semkey;int semid, pfd, fv;struct sembuf sbuf;char *lgn;char filename[PATH_MAX+1];struct stat outstat;struct passwd *pw;/* Get unique key for semaphore. */if ((semkey = ftok("/tmp", 'b')) == (key_t) -1) {perror("IPC error: ftok"); exit(1);}/* Get semaphore ID associated with this key. */if ((semid = semget(semkey, 0, 0)) == -1) {/* Semaphore does not exist - Create. */if ((semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | S_IRUSR |S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != -1){ /* Initialize the semaphore. */ sbuf.sem_num = 0; sbuf.sem_op = 2; /* This is the number of runswithout queuing. */ sbuf.sem_flg = 0; if (semop(semid, &sbuf, 1) == -1) { perror("IPC error: semop"); exit(1); }}else if (errno == EEXIST) { if ((semid = semget(semkey, 0, 0)) == -1) { perror("IPC error 1: semget"); exit(1); }}else { perror("IPC error 2: semget"); exit(1);}}}
2.网上找到的例子
#include <sys/types.h>#include <sys/shm.h>#include <sys/sem.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <signal.h> #define SHMDATASIZE 1000#define BUFFERSIZE (SHMDATASIZE - sizeof ( int ))#define SN_EMPTY 0#define SN_FULL 1#define TRUE 1int deleteSemid = 0;// 必须自己定义一个 union, 否则编译不过union semun{ int val ; struct semid_ds * buf ; unsigned short int * array ; struct seminfo * __buf ;};void server ();void client ( int shmid);void delete ();void sigdelete ( int signum);void locksem ( int semid, int semnum);void unlocksem ( int semid, int semnum);//void waitzero(int semid, int semnum);void clientwrite ( int shmid, int semid, char *buffer); int safesemget (key_t key, int nsems, int semflg);int safesemctl ( int semid, int semnum, int cmd, union semun arg);int safesemop ( int semid, struct sembuf *sops, unsigned nsops);int safeshmget (key_t key, int size, int shmflg);void * safeshmat ( int shmid, const void *shmaddr, int shmflg);int safeshmctl ( int shmid, int cmd, struct shmid_ds *buf); int main ( int argc, char *argv[]){ if (argc < 2) { // 一个参数启动 server server(); } else { // 多个参数启动 client client( atoi (argv[1])); //atoi 把字符串转为整形数 } return 0;} void server (){ union semun sunion; // 与 semctl 中的 cmd 参数有关 int semid, shmid; void *shmdata; char *buffer; semid = safesemget(IPC_PRIVATE, 2, SHM_R | SHM_W); // 创建一个信号集 deleteSemid = semid; atexit (&delete); // 注册终止函数 signal (SIGINT, &sigdelete); // 设置某一信号的对应动作 , 程序终止 (interrupt) 信号 , 在用户键入 INTR 字符 ( 通常是 Ctrl-C) 时发出 sunion. val = 1; // 将一个二元信号量初始化为 1 safesemctl(semid, SN_EMPTY, SETVAL, sunion); //SETVAL 设置信号量集中的一个单独的信号量的值 sunion. val = 0; // 将一个二元信号量赋值为 0 safesemctl(semid, SN_FULL, SETVAL, sunion); shmid = safeshmget(IPC_PRIVATE, SHMDATASIZE, IPC_CREAT | SHM_R | SHM_W); // 创建共享内存 shmdata = safeshmat(shmid, 0, 0); // 将该共享内存映射进进程的虚存空间 safeshmctl(shmid, IPC_RMID, NULL); // 将该共享内存标志为已销毁的,这样在使用完毕后,将被自动销毁 *( int *) shmdata = semid; // 将信号量的标识符写入共享内存,以通知其它的进程 buffer = shmdata + sizeof ( int ); //buf 数据的起始地址 , 因为第一个地址装载了 semid printf ( "server is running with SHM id ** %d **\n" , shmid); while (TRUE) { printf ( "waiting until full... " ); fflush (stdout); locksem(semid, SN_FULL); // 获得共享资源 printf ( "done. \n" ); printf ( "message received: %s.\n" , buffer); unlocksem(semid, SN_EMPTY); }} void client ( int shmid){ int semid; void *shmdata; char *buffer; shmdata = safeshmat(shmid, 0, 0); // 将该共享内存映射进进程的虚存空间 , 这时共享内存的第一个数据是之前 sercer 写入的 semid = *( int *) shmdata; // 获得 server 创建的共享内存 buffer = shmdata + sizeof ( int ); //buf 数据的起始地址 , 因为第一个地址装载了 semid printf ( "client operational: shm id is %d,sem id is %d\n" , shmid, semid); while (TRUE) { char input[3]; printf ( "\n menu \n1.Send a message\n" ); printf ( "2.Exit\n" ); fgets (input, sizeof (input), stdin); switch (input[0]) { case '1' : clientwrite(shmid, semid, buffer); break ; case '2' : exit (0); break ; } }} int safesemget (key_t key, int nsems, int semflg){ int retval; if ((retval = semget(key, nsems, semflg)) == -1) { printf ( "semget error: %s. \n" , strerror(errno)); exit (254); } return retval;} void sigdelete ( int signum){ exit (0);} int safesemctl ( int semid, int semnum, int cmd, union semun arg){ int retval; if ((retval = semctl(semid, semnum, cmd, arg)) == -1) { printf ( "semctl error: %s. \n" , strerror(errno)); exit (254); } return retval;} int safeshmget (key_t key, int size, int shmflg){ int retval; if ((retval = shmget(key, size, shmflg)) == -1) { printf ( "shmget error: %s. \n" , strerror(errno)); exit (254); } return retval;} void * safeshmat ( int shmid, const void *shmaddr, int shmflg){ void * retval; if ((retval = shmat(shmid, shmaddr, shmflg)) == ( void *) -1) { printf ( "shmat error: %s. \n" , strerror(errno)); exit (254); } return retval;} int safeshmctl ( int shmid, int cmd, struct shmid_ds *buf){ int retval; if ((retval = shmctl(shmid, cmd, buf)) == -1) { printf ( "shmctl error: %s. \n" , strerror(errno)); exit (254); } return retval;} void locksem ( int semid, int semnum){ struct sembuf sb; // 指定调用 semop 函数所做操作 sb. sem_num = semnum; // 指定要操作的信号量 sb. sem_op = -1; // 要执行的操作 ,<0 表示进程希望使用资源 sb. sem_flg = SEM_UNDO; // 标志 safesemop(semid, &sb, 1);} void unlocksem ( int semid, int semnum){ struct sembuf sb; // 指定调用 semop 函数所做操作 sb. sem_num = semnum; // 指定要操作的信号量 sb. sem_op = 1; // 要执行的操作 ,>0 表示對進程的资源使用完畢 , 交回该资源 sb. sem_flg = SEM_UNDO; // 标志 safesemop(semid, &sb, 1);} int safesemop ( int semid, struct sembuf *sops, unsigned nsops){ int retval; if ((retval = semop(semid, sops, nsops)) == -1) { printf ( "semop error: %s. \n" , strerror(errno)); exit (254); } return retval;} void clientwrite ( int shmid, int semid, char *buffer){ printf ( "waiting until empty..." ); fflush (stdout); locksem(semid, SN_EMPTY); // 这个过程是在等待共享内存资源 printf ( "done.\n" ); printf ( "enter message: " ); fgets (buffer, BUFFERSIZE, stdin); unlocksem(semid, SN_FULL);} void delete (){ printf ( "\n master exiting; deleting semaphore %d. \n" , deleteSemid); if ((semctl(deleteSemid, 0, IPC_RMID, 0)) == -1) //IPC_RMID 删除该信号量 printf ( "error releasing semaphore. \n" );}
例子使用说明:
运行服务端:./app 不带参数
server is running with SHM id ** 8257564 **
waiting until full... 1
运行客户端./APP 8257564
menu
1.Send a message
2.Exit
选择1发送信息
waiting until empty...done.
enter message:
输入信息并按回车
选择2退出客户端
^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^
Posix机制
一.头文件
#include <semaphore.h>
二.结构体
三.API函数
无名信号量:
1.初始化无名信号量
int sem_init(sem_t *sem, int pshared, unsigned value);
参数:
sem: 信号量标识ID
pshared:为0表示同一进程内的线程共享,非0值则为进程间共享
value:
2.销毁无名信号量
int sem_destroy(sem_t *sem);
sem: 信号量标识ID
有名信号量:
3.初始化并使用有名信号量
sem_t *sem_open(const char *name, int oflag, /* mode_t mode, unsigned int *value */);
参数:
name:信号量外部别名
oflag:标志
mode:权限(可选参数)
value:信号量初始值
4.关闭有名信号量
int sem_close(sem_t *sem);
参数:
sem: 信号量标识ID
5.移除有名信号量
int sem_unlink(const char *name);
参数:
name:信号量外部别名
V操作
6.释放信号量
int sem_post(sem_t *sem);
参数:
sem: 信号量标识ID
P操作
7.申请信号量
int sem_wait(sem_t *sem);
参数:
sem: 信号量标识ID
8.尝试申请信号量
int sem_trywait(sem_t *sem);
参数:
sem: 信号量标识ID
四.例子
#include <unistd.h> #include <pthread.h> #include <stdio.h> #include <errno.h> #include <semaphore.h> sem_t add; sem_t sub; void* thread0(void *param) { while(1){ sem_wait( &add);while(1){char input[3];fgets(input,sizeof(input),stdin);if(input[0]==']'){(*(int*)param) += 1;continue;}else{break;}} printf("Thread0: %d\n", *(int*)param); sem_post( &sub); sleep(1); } return NULL; } void* thread1(void *param) { while(1){ sem_wait( &sub); while(1){char input[2];fgets(input,sizeof(input),stdin);if(input[0]=='['){(*(int*)param) -= 1;continue;}else{break; }} printf("Thread1: %d\n", *(int*)param); sem_post(&add); sleep(1); } return NULL; } int main() { int sum = 0; int i; sem_init(&add, 0, 100); sem_init(&sub, 0, sum); pthread_t ths[2]; pthread_create(&ths[0], NULL, thread0, (void*)&sum); pthread_create(&ths[1], NULL, thread1, (void*)&sum); for(i = 0; i < 2; ++ i){ pthread_join(ths[i], NULL); } }