信号量
信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源访问的同步。
信号量的值大于或等于0时表示可供并发进程使用的资源实体数;小于0时代表正在等待使用临界资源的进程数
1、信号集的创建或打开
int semget(key_t key, int sems, int sem_flags);
key 是由ftok()的到的键值。 nsems指明要创建的信号集包含的信号个数,改参数后面还会提到。semflg为操作标志
- IPC_CREATE:调用semget()时,会将本信号集中的key值和其他信号集中的key进行对比,如果存在相同的key,说明信号集已存在,此时返回该信号集的标识符,否则新建一个信号集并返回其标识符。
- IPC_EXCL:该宏和IPC_CREATE一起使用,否则没有意义。当 semflg取PC_CREATE|IPC_EXCL时,表示如果发现信号集已经存在,则返回错误,错误码
为EEXIST。
2、信号量的操作
int semop(int semid, struct sembuf *sops, size_t nsops);
semid为信号集的标识符;sops指向进行操作的结构体首地址;nsops指出将要进行操作的信号个数。semop函数调用成功返回0,失败返回-1。
struct sembuf{
short sem_num;//信号在信号集中的索引
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg;//操作标志IPC_NOWAIT,IPC_UNDO 详细请见UNIX高级环境编程
};
取值范围 | 操作意义 |
---|---|
sem_op > 0 | 信号机上sem_op的值,表示进程释放控制的资源 |
sem_op | 如果没有设置IPC_NOWAIT则调用进程进入睡眠状态,直到信号值为0;否则不会睡眠,直接返回EAGAIN |
sem_op < 0 | 信号加上sem_op的值。若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN |
信号集的控制
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */);
semid为信号集的标识符;semnum标识一个特定的信号;cmd指明控制操作的类型;“…”说明函数的参数时可选的
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf; /*for IPC_STAT and IPC_SET*/
unsigned short *array; /*for GETALL and SETALL*/
};
cmd参数指定下列命令中的一种
共享内存
共享内存就是分配一块能被其他进程访问的内存。
共享内存区的创建
int shmget(key_t key, size_t size, int shmflg);
IPC_CREATE: 调用sheget时,系统将此值与其他所有共享内存区的key进行比较,如果存在相同的key,说明共享内存区已存在,此时返回该共享内存区的标识符,否则新建一个共享内存区并返回其标识符。
IPC_EXC: 该宏必须和IPC_CREATE 一起使用,否则没有意义。当shmflg取IPC_CREATE|IPC_EXCL时,表示如果发现信号集已经存在,则返回-1,错误码为EEXIST。
共享内存区的操作
在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。
void* shmat(int shmid, const void* shmaddr, int shmflag);
shmid为shmget函数的返回值;参数shmflg为存取权限标志;参数shmaddr为共享内存的附加点,其不同取值说明如下:
进程结束使用共享内存后,要通过shmdt断开与共享内存区的连接。
int shmdt(const void* shmaddr);
参数shmaddr为shmat函数的返回值。调用成功返回0,否则返回-1。
共享内存区的控制
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid为共享内存区的标识符;buf为指向shmid_ds结构体的指针;cmd为操作标志位
- IPC_RMID : 从系统中删除由shmid标识的共享内存区。
- IPC_SET : 设置共享内存区的shmid_ds结构。
- IPC_STAT : 读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。
应用实例
ipc.h
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<sys/msg.h>
#include<signal.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
key_t Ftok(const char *pathname, int proj_id)
{
key_t key = ftok(pathname, proj_id);
if(key == -1)
{
perror("ftok.");
exit(1);
}
return key;
}
int Semget(key_t key, int nsems, int semflg)
{
int id = semget(key, nsems, semflg);
if(id == -1)
{
perror("semget.");
exit(1);
}
return id;
}
int Shmget(key_t key, size_t size, int shmflg)
{
int id = shmget(key, size, shmflg);
if(id == -1)
{
perror("shmget.");
exit(1);
}
return id;
}
void* Shmat(int shmid, const void *shmaddr, int shmflg)
{
void *addr = shmat(shmid, shmaddr, shmflg);
if(addr == (void*)-1)
{
perror("shmat.");
exit(1);
}
return addr;
}
int Msgget(key_t key, int msgflg)
{
int id = msgget(key, msgflg);
if(id == -1)
{
perror("msgget.");
exit(1);
}
return id;
}
cli.c
#include"ipc.h"
void cli_exit(int signo)
{
printf("Client exit. . .\n");
exit(0);
}
// ./cli . 0xff
int main(int argc, char *argv[])
{
struct sigaction act;
act.sa_handler = cli_exit;
key_t shm_key = Ftok(argv[1], atoi(argv[2]));
int shm_id = Shmget(shm_key, 0, 0);
char *addr = (char*)Shmat(shm_id, NULL, 0);
int sem_id = Semget(shm_key, 0, 0);
struct sembuf v = {1, 1, SEM_UNDO};
struct sembuf p = {0, -1, SEM_UNDO};
sigaction(SIGINT, &act, NULL);
while(1)
{
semop(sem_id, &p, 1);
printf("Ser:>%s\n",addr);
printf("Cli:>");
scanf("%s",addr);
semop(sem_id, &v, 1);
}
return 0;
}
ser.c
#include"ipc.h"
int shm_id, sem_id;
char *addr;
void ser_exit(int signo)
{
semctl(sem_id, 0, IPC_RMID);
semctl(sem_id, 1, IPC_RMID);
shmdt(addr);
shmctl(shm_id, IPC_RMID, NULL);
printf("Server exit . . .\n");
exit(0);
}
// ./ser . 0xff
int main(int argc, char *argv[])
{
struct sigaction act;
act.sa_handler = ser_exit;
key_t shm_key = Ftok(argv[1], atoi(argv[2]));
shm_id = Shmget(shm_key, 1024, IPC_CREAT|IPC_EXCL|0755);
addr = (char*)Shmat(shm_id, NULL, 0);
sem_id = Semget(shm_key, 2, IPC_CREAT|IPC_EXCL|0755);
union semun init;
init.val = 0;
semctl(sem_id, 0, SETVAL, init);
semctl(sem_id, 1, SETVAL, init);
struct sembuf v = {0, 1, SEM_UNDO}; //第一个参数代表0标志位,第二个是v操作后加的值
struct sembuf p = {1, -1, SEM_UNDO};
//第三个参数分为IPC_NOWAIT和SEM_UNDO
sigaction(SIGINT, &act, NULL);
while(1)
{
printf("Ser:>");
scanf("%s",addr);
semop(sem_id, &v, 1);
semop(sem_id, &p, 1);
printf("Cli:>%s\n",addr);
}
return 0;
}
运行示例
server端
client端