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 1
int 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); 
    } 
} 





 

 

你可能感兴趣的:(linux 进程间通讯--信号量)