目录
- IPC
- 管道
- pipe() 创建一个新管道
- mkfifo() 创建一个FIFO
- Socket
- 通信原理:
- socket() 创建一个新的socket
- bind() 将socket绑定到地址
- listen() 监听接入连接
- accept() 接受连接
- connect() 连接到对等的socket
- recvfrom() 接收数据报
- sendto() 发送数据报
- 主机字节序与网络字节序相互转换函数
- IP地址转换函数
- System V IPC对象编程接口总结
- POSIX IPC对象编程接口总结
- 共享内存
- System V 共享内存
- ftok() 产生一个唯一的key
- 示例
- shmget 创建或打开一个共享内存段
- shmat 把共享内存的地址映射到调用进程的地址空间
- shmdt 断开与共享内存的连接
- shmctl 共享内存管理
- POSIX 共享内存
- shm_open() 创建或打开一个新的或既有信号量
- shm_unlink() 删除共享内存对象
- 内存映射
- 1.mmap() 创建一个映射
- 2.munmap() 接触映射区域
- 3.msync() 同步映射区域
- 消息队列
- System V 消息队列
- msgget() 创建或打开一个消息队列
- msgsnd() 向消息队列中写入一条消息
- msgrcv() 从消息队列中取出一条消息
- msgctl() 删除消息队列中的消息
- POSIX 消息队列
- mq_open() 创建或打开一个新的或既有信号量
- mq_close() 关闭一个消息队列
- mq_unlink() 删除一个消息队列
- mq_send() 发送消息
- mq_receive() 发布一个信号量
- mq_notify() 消息通知
- 信号量
- System V 信号量
- semget() 创建或打开一个信号量集
- semctl() 信号量控制操作(常用于设置信号量的初始值和销毁信号量)
- semop() 信号量操作
- 案例(哲学家进餐问题)
- POSIX 信号量
- sem_open() 创建或打开一个新的或既有信号量
- sem_close() 关闭一个信号量
- sem_unlink() 删除一个命名信号量
- sem_wait() 等待一个信号量
- sem_post() 发布一个信号量
- sem_getvalue() 获取当前信号量的值
- sem_init() 初始化一个未命名信号量
- sem_destroy() 销毁一个未命名信号量
- 案例
- unlink.c 删除创建的信号量
- producer1.c 生产者1
- producer2.c 生产者2
- consumer1.c 消费者1
- consumer2.c 消费者2
IPC
Inter-Process Communication(进程间通信)
管道
pipe() 创建一个新管道
#include <unistd.h>
int pipe(int filedes[2])
返回值: 成功返回一个0,失败返回-1。
成功调用pipe会在数组filedes中返回两个打开的文件描述符:一个表示管道的读端(filedes[0]),一个表示管道的写端(filedes[1])。与所有文件描述符一样,可以使用read()和write()系统调用在管道上执行I/O。
mkfifo() 创建一个FIFO
#include <sys/stat.h>
int mkfifo(const char* pathname,mode_t mode)
pathname: FIFO名。
mode: 指定FIFO权限。
返回值: 成功返回一个0,失败返回-1。
Socket
通信原理:
网络通信的函数接口:封装了传输层协议:tcp,udp
socket编程就是网络I/O编程,需要读写操作
套接字创建成功后会在内存开辟一块缓冲区,返回文件描述符。
socket() 创建一个新的socket
#include <sys/socket.h>
int socket(int domain,int type,int protocol)
domain: 指定通信domain,取值如下:
AF_UNIX | 同一主机 :scokaddr_un |
AF_INET | IPv4::sockaddr:in |
AF_INET6 | IPv6::sockaddr:in6 |
type: 指定socket类型:SOCK_STREAM流类型,SOCK_DGRAM数据报类型。
protocol: 一般指定为0。
返回值: 成功返回文件描述符,失败返回-1。
bind() 将socket绑定到地址
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr* addr, socklen_t addrlen)
//通用socket地址结构
struct sockaddr {
sa_family_t sa_family; //
char sa_data[14]; //socket 地址
}
sockfd: socket()获得的文件描述符。
addr: 指向该socket绑定到的地址结构。
addrlen: 指定地址结构大小。
返回值: 成功返回文件描述符,失败返回-1。
listen() 监听接入连接
#include <sys/socket.h>
int listen(int sockfd,int backlog)
backlog: 限制未决连接数量。
返回值: 成功返回文件描述符,失败返回-1。
accept() 接受连接
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen)
返回值: 成功返回已连接socket的文件描述符,失败返回-1。
connect() 连接到对等的socket
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
返回值: 成功返回文件描述符,失败返回-1。
recvfrom() 接收数据报
#include <sys/socket.h>
ssize_t recvfrom(int sockfd,void* buffer,size_t length,int flags,struct sockaddr* src_addr,socklen_t* addrlen)
返回值: 成功返回收到的字节数,失败返回-1,EOF返回0。
sendto() 发送数据报
#include <sys/socket.h>
ssize_t sendto(int sockfd,void* buffer,size_t length,int flags,const struct sockaddr* dest_addr,socklen_t* addrlen)
返回值: 成功返回发送的字节数,失败返回-1。
主机字节序与网络字节序相互转换函数
//主机字节顺序 --> 网络字节顺序
#include <arpa/inet.h>
端口 uint16_t htons(uint16_t hostshort);
IP uint32_t htonl(uint32_t hostlong);
//网络字节顺序 --> 主机字节顺序
端口:uint16_t ntohs(uint16_t netshort);
IP:uint32_t ntohl(uint32_t netlong);
IP地址转换函数
#include <arpa/inet.h>
//本地IP转网络字节序 字符串 --> int(大端方式存储)
int inet_pton(int af, const char *src,void *dst);
//af:指定当前所使用的协议,AF_INET(IPV4),AF_INET6(IPV6)
//src:IP地址(点分十进制)
//dst:传出,网络字节序的IP地址
//网络字节序转本地IP int -> 字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
//size:dst的大小
System V IPC对象编程接口总结
POSIX IPC对象编程接口总结
与打开文件一样,POSIX IPC对象也有引用计数——内核会维护对象上的打开计数引用。所以每次使用完POSIX IPC对象需要手动调用unlink来删除该对象。
共享内存
System V 共享内存
ftok() 产生一个唯一的key
#include <sys/ipc.h>
key_t ftok(char* pathname,int proj)
proj: 返回值通过取proj最低8位,计算后得到key值,一般传入char类型的值。
返回值: 成功返回一个整数值,失败返回-1。
示例
key_t key = ftok("/mydir/myfile","x");
if(key == -1)
errExit("ftok");
int id = msgget(key,IPC_CREAT|SIRUSR|S_IWUSR);
if(id == -1)
errExit("msgget");
shmget 创建或打开一个共享内存段
#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key,size_t size,int shmflg)
key: shmget会转换成共享内存段的唯一标识。
size: 需要分配的段的字节数。
shmflg: 指定施加于共享段的权限,取值如下:
IPC_CREAT | 如果不存在与指定的key对应的段,就创建一个新段 |
IPC_EXCL | 如果同时指定了IPC_CREAT并且与指定key对应的段已经存在,就返回EEXIST错误 |
SHM_HUGETLB(Linux2.起) | 特权进程能够使用该标记创建一个使用巨页的共享内存段 |
SHM_NORESERVE(Linux2.6.15起) | 允许过度利用空间 |
返回值: 成功返回共享内存唯一标识,失败返回-1。
shmat 把共享内存的地址映射到调用进程的地址空间
#include <sys/types.h>
#include <sys/shm.h>
void* shmat (int shmid,const void* shmaddr,int shmflg)
shmid: 共享内存标识符。
shmaddr: 指定共享内存在进程内存中的位置,如果为NULL则由内核自己决定。
shmflg: SHM_RDONLY只读模式,其他为读写模式。
返回值: 成功返回附加好的共享内存地址,失败返回-1。
shmdt 断开与共享内存的连接
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void* shmaddr)
返回值: 成功返回0,失败返回-1。
shmctl 共享内存管理
#include <sys/types.h>
#include <sys/shm.h>
int shmctl (int shmid,int cmd,struct shmid_ds* buf)
cmd: 要对共享内存进行的操作,IPC_RMID:如果当前进程没有附加该段,立刻删除,否则就在所有进程与该段分离后再执行删除。IPC_STAT:将与这个共享关联的shmid_ds数据结构的副本放到buf指向的缓冲区中,IPC_SET:使用buf指向的缓冲区的值来更新与这个共享内存段相关的shmid_ds数据结构中被选中的字段。
返回值: 成功返回0,失败返回-1。
POSIX 共享内存
shm_open() 创建或打开一个新的或既有信号量
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
int shm_open(const char* name,int oflag,mode_t mode)
name: 唯一标识共享内存对象。
oflag: 位掩码,取值如下:
O_CREAT | 对象不存在时创建对象 |
O_EXCL | 如果同时指定O_CREAT,并且对象已存在,就返回EEXIST错误 |
O_RDONLY | 只读访问 |
O_RDWR | 读写访问 |
O_TRUNC | 将对象长度截断为零 |
mode: 位掩码,指定施加于信号量上的权限。
value: 指定了信号量初始值。
返回值: 成功返回文件描述符,失败返回-1。
shm_unlink() 删除共享内存对象
#include <sys/mman.h>
int shm_unlink(const char* name)
name: 唯一标识共享内存。
返回值: 成功返回0,失败返回-1。
内存映射
mmap()系统调用在调用进程的虚拟地址空间中创建一个新内存映射。映射分为两种
1.文件映射:将一个文件的一部分直接映射到内存中,通过对内存的操作来访问文件。
2.匿名映射:没有对应的文件,这种映射的分页会被初始化为0。
1.mmap() 创建一个映射
#include<sys/mman.h>
void* mmap(void* addr, size_t length,int prot,int flag,int fd,off_t offset);
addr: 指定映射被放置的虚拟地址。如果设为NULL,则由内核自动选取。
length: 指定映射的字节数(虽然length无需是系统分页大小的倍数,但内核会以分页大小为单位来创建映射)。
prot: 位掩码,指定了映射上的保护信息。取值要么是PROT_NONE,要么是下表中其他三个标记的组合。
值 | 描述 |
PROT_NONE | 区域无法访问 |
PROT_READ | 区域内容可读 |
PROT_WRITE | 区域内容可写 |
PROT_EXEC | 区域内容可执行 |
flag: 控制映射操作的位掩码,取值见下表。
MAP_PRIVATE | 私有映射,内容变更对其他进程不可见 |
MAP_SHARED | 共享映射,内容对其他进程可见 |
fd: 标识被映射文件的描述符。
offset: 指定映射在文件中的起点 。
返回值: 成功返回映射起始地址,失败返回MAP_FAILED。
2.munmap() 接触映射区域
#include <sys/mman.h>
int munmap(void* addr,size_t length)
addr: 映射的起始地址。
length: 映射区大小。
返回值: 成功返回0失败返回-1.
3.msync() 同步映射区域
#include <sys/mman.h>
int msync(void* addr,size_t length,int flags)
addr: 映射的起始地址。
length: 映射区大小。
flags: 参数可取下列中值中的一个。
MS_SYNC | 同步文件写入;会阻塞,直到被修改过的内容被写入完毕 |
MS_ASYNC | 异步文件写入;内存区域中被修改的分页会在后面某个时刻被写入磁盘并立即对在相应文件区域中执行read的其他进程可见 |
另外,MS_SYNC操作执行后,内存与磁盘同步,而MS_ASYNC执行后,内存与高速缓冲同步。
返回值: 成功返回0失败返回-1.
一些高级或不常用系统调用待补充。案例见POSIX信号量。
消息队列
System V 消息队列
msgget() 创建或打开一个消息队列
#include <sys/types.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
key: 整数值,IPCget调用会将一个key转换成相应的IPC标识符。保证key唯一:1.随机选取一个整数值。2.将IPC_PRIVATE常量作为key值,这样每个调用都会创建一个全新的IPC对象。3.使用ftok函数生成一个key(用法见 System V 共享内存)。
msgflg: 施加于消息队列上的权限。IPC_CREAT:如果没有与指定的key对应的消息队列,就创建一个新队列。如果同时指定了IPC_CREAT和IPC_EXCL,并且指定的key对应的队列已经存在,则返回EEXIST错误。
返回值: 成功返回消息队列标识符,失败返回-1。
msgsnd() 向消息队列中写入一条消息
#include <sys/types.h>
#include <sys/msg.h>
int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg)
//通用消息结构体
struct msgbuf {
long mtype; /*消息类型 必须大于0*/
char metxt[1]; /*消息文本*/
}
msqid: 消息队列描述符(由msgget函数得到)。
msgp: 指向消息缓冲区的指针,此位置用来暂存消息,是一个结构体(msgbuf)。
msgsz: 要发送的消息长度,可以用:msgsz = sizeof(struct msgbuf) - sizeof(long)计算。
msgflg: 位掩码,IPC_NOWAIT:执行一个非阻塞的发送操作,通常,当消息队列满时,会阻塞知道队列中有足够空间来存放这条消息,如果自定IPC_NOWAIT,该调用就会立刻返回EAGAIN错误。0:表示忽略。不指定表示阻塞。
返回值: 成功返回消息队列标识符,失败返回-1。
msgrcv() 从消息队列中取出一条消息
#include <sys/types.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid,void* msgp,size_t msgsz,long msgtyp,int msgflg)
msgtyp: =0:返回队列最早的一个消息,>0: 返回器类型为msgtype的第一个消息,<0: 返回器类型小于或等于msgtype参数的绝对值的最小的一个消息。
msgflg: 控制函数行为的标志,取值可以是0,表示忽略,或IPC_NOWAIT和MSGEXCEPT组合。IPC_NOWAIT,如果消息队列为空返回ENOMSG。。
返回值: 成功返回消息队列标识符,失败返回-1。
msgctl() 删除消息队列中的消息
#include <sys/types.h>
#include <sys/msg.h>
ssize_t msgctl(int msqid,int cmd,struct msqid_ds* buf)
cmd: 要对消息进行的操作,IPC_RMID:立刻删除消息队列对象及其关联的msqid_ds数据结构,IPC_STAT:将于这个消息队列关联的msqid_ds数据结构的副本放到buf指向的缓冲区中,IPC_SET:设定消息队列的msqid_ds数据中的msg_perm成员,设定的值由buf指向的msqid_ds给出。
返回值: 成功返回消息队列标识符,失败返回-1。
POSIX 消息队列
POSIX消息队列被实现成了虚拟文件系统中的i-node,并且消息队列描述符和打开着的消息队列描述符分别被实现成了文件描述符和打开着的文件描述符。
mq_open() 创建或打开一个新的或既有信号量
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
mqd_t* mq_open(const char* name,int oflag,.../*mode_t mode,struct mq_attr* attr */)
name: 唯一标识消息队列。
oflag: 位掩码,取值如下:
O_CREAT | 对象不存在时创建对象 |
O_EXCL | 如果同时指定O_CREAT,并且对象已存在,就返回EEXIST错误 |
O_RDONLY | 只读访问 |
O_WRONLY | 只写访问 |
O_RDWR | 读写访问 |
O_NONBLOCK | 以非阻塞模式打开 |
如果打开一个既有队列则不需要下面两个参数,如果创建一个新队列则还需要下面两个参数。
mode: 位掩码,指定施加于信号量上的权限。
attr: 如果attr为NULL,那么将使用实现定义的默认特性创建队列。
返回值: 成功返回队列描述符,失败返回-1。
mq_close() 关闭一个消息队列
关闭一个队列,并不会删除一个队列,要删除信号量需要调用mq_unlink
#include <mqueue.h>
int mq_close(mqd_t* mqdes)
返回值: 成功返回0,失败返回-1。
mq_unlink() 删除一个消息队列
#include <mqueue.h>
int mq_unlink(const char* name)
返回值: 成功返回0,失败返回-1。
mq_send() 发送消息
#include <mqueue.h>
#include <time.h>
int mq_send(mqd_t mqdes,const char* msg_ptr,size_t msg_len,unsigned int msg_prio)
int mq_timedsend(mqd_t mqdes,const char* msg_ptr,size_t msg_len,const struct timespec* abs_timeout,unsigned int msg_prio)
msg_len: 指定msg_ptr指向的消息的长度。
msg_prio: 指定消息优先级,设为0表示不指定优先级。
返回值: 成功返回0,失败返回-1。
mq_receive() 发布一个信号量
#include <mqueue.h>
#include <time.h>
ssize_t mq_receive(mqd_t mqdes,const char* msg_ptr,size_t msg_len,unsigned int* msg_prio)
ssize_t mq_timedreceive(mqd_t mqdes,const char* msg_ptr,size_t msg_len,const struct timespec* abs_timeout,unsigned int* msg_prio)
msg_len: 指定msg_ptr指向的消息的可用字节数。
返回值: 成功返回消息的字节数,失败返回-1。
mq_notify() 消息通知
#include <mqueue.h>
int mq_notify(mqd_t mqdes,const struct sigevent* notification)
返回值: 成功返回消息的字节数,失败返回-1
信号量
System V 信号量
semget() 创建或打开一个信号量集
#include <sys/types.h>
#include <sys/sem.h>
int semget(key_t key,int nsems,int semflg)
key: 用一种方法生成的键,具体见System V 共享内存。
nsems: 如果创建一个新信号集,那么nsems会指定集合中信号量的数量,并且其值必须大于0,如果获取一个既有集标识符,那么nsems必须要小于或等于集合大小,否则发生EINVAL错误。
semflg: 取值如下:IPC_CREAT,如果不存在与指定的key相关联的集合,就创建一个新集合。IPC_EXCL,如果同时指定了IPC_CREAT并且与指定的key相关的集合已经存在,那么返回EEXIST错误。
返回值: 成功返回信号量集标识,失败返回-1。
semctl() 信号量控制操作(常用于设置信号量的初始值和销毁信号量)
#include <sys/types.h>
#include <sys/sem.h>
int semctl(int semid,int semnum,int cmd,.../* union semun arg */)
//semun数据结构
union semun
{
int val;
struct semid_ds* buf;
unsigned short* arry;
}
semid: 操作所施加的信号集的标识符。
semnum: 对于在单个信号量上执行的操作,该参数标识出了集合中的具体信号量,对于其他操作则会忽略这个参数,并且可以将其设置为0。
cmd: 指定了需要执行的操作,IPC_RMID:销毁信号量,SETVAL:初始化信号量。
arg : 一些特定的操作,需要传递该参数。
返回值: 成功返回非负整数,失败返回-1。
semop() 信号量操作
#include <sys/types.h>
#include <sys/sem.h>
int semop(int semid,struct sembuf* sops,unsigned int nsops)
int semtimedop(int semid,struct sembuf* sops,unsigned int nsops,struct timespec* timeout)//超时返回版
//数组中元素结构
struct sembuf {
unsigned short sem_num;//信号量集的个数,单个集合设置为0
short sem_op;//需要执行的操作:-1 p操作,1 v操作
short sem_flg;//一般设置为SEM_UNDO
}
sops: 该参数是一个指向数组的指针,数组中包含了需要执行的操作。
nsops: 该参数给出了数组的大小(至少包含一个元素)。
返回值: 成功返回0,失败返回-1。
sem_op: 如果sem_op大于0,就讲sem_op的值加到信号量值上,如果sem_op等于0,就检查信号量的值是否等于0,如果dengyu0,那么操作讲立即结束,否则sem_op()就会阻塞知道信号量值变成0为止,如果sem_op小于0,那么讲信号量的值减去sem_op。
案例(哲学家进餐问题)
//哲学家进餐模型
//5位哲学家与邻居对其中间的筷子是互斥关系
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds* buf;
unsigned short* arry;
};
int wait(int semid)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(semid,&sem_b,1) == -1)
{
printf("wait semop error\n");
return 0;
}
return 1;
}
int post(int semid)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(semop(semid,&sem_b,1) == -1)
{
printf("post semop error!\n");
return 0;
}
return 1;
}
int main(int argc,char** argv)
{
if(argc != 2)
{
printf("参数格式不正确\n");
return -1;
}
/*
* 定义互斥信号量数组,用于实现对筷子的互斥访问。
* 对哲学家编号按0~4编号,哲学家i左边的筷子编号为i,右边的筷子编号为(i + 1) % 5。
*/
key_t key[5];
int chopsticks[5];
for(int i = 0; i < 5; ++i)
{
key[i] = ftok(".",i);
if((chopsticks[i] = semget(key[i],1,IPC_CREAT|0640)) == -1)
{
printf("信号量创建失败\n");
return -1;
}
union semun sem_union;
sem_union.val = 1;
if(semctl(chopsticks[i],0,SETVAL,sem_union) < 0) //初始化信号量值为1
{
printf("信号量初始化失败\n");
return -1;
}
}
key_t keyMutex = ftok(".",5);
int mutex = -1;
if((mutex = semget(keyMutex,1,IPC_CREAT|0640)) == -1)
{
printf("互斥信号量创建失败\n");
return -1;
}
union semun sem_union;
sem_union.val = 1;
if(semctl(mutex,0,SETVAL,sem_union) < 0)
{
printf("互斥信号量初始化失败\n");
return -1;
}
int i = strtol(argv[1],NULL,10);
if(i < 0 || i >=5)
{
printf("参数输入错误\n");
return -1;
}
while(1)
{
wait(mutex); //仅当一个哲学家左右两支筷子都可以使用时,才允许他抓起筷子
wait(chopsticks[i]); //拿左边的筷子
wait(chopsticks[(i + 1) % 5]); //拿右边的筷子
post(mutex);
printf("哲学家%d开始吃饭\n",i);
sleep(2);
post(chopsticks[i]);
post(chopsticks[(i + 1) % 5]);
// printf("哲学家%d正在思考\n",i);
sleep(3);
}
}
POSIX 信号量
SUSv3规定了两种信号量:
1.命令信号量:通过使用相同名字调用sem_open(),不相关的进程能够访问同一个信号量。
2.未命名信号量: 位于内存种预先商定的位置处,当在进程/线程间共享时,信号量必须位于一个共享内存区域中。
sem_open() 创建或打开一个新的或既有信号量
创建的信号量位于/etc/shm/处。
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t* sem_open(const char* name,int oflag,.../*mode_t mode,unsigned int value */)
name: 唯一标识信号量。
oflag: 位掩码,确定了是打开已有信号量还是创建新的信号量。如果为0,那么访问一个既有信号量。如果指定了O_CREAT,并且给定的name对应的信号量不存在,就创建一个信号量。如果同时指定O_CREAT|O_EXCL,并且给定name对应的信号量已经存在,那么sem_open失败。如果打开既有信号量只需要两个参数,如果创建新的信号量就需要后面两个参数。
mode: 位掩码,指定施加于信号量上的权限。
value: 指定了信号量初始值。
返回值: 成功返回信号量指针,失败返回SEM_FAILED。
sem_close() 关闭一个信号量
关闭一个信号量,并不会删除一个信号量,要删除信号量需要调用sem_unlink
#include <semaphore.h>
int sem_close(sem_t* sem)
返回值: 成功返回0,失败返回-1。
sem_unlink() 删除一个命名信号量
#include <semphore.h>
int sem_unlink(const char* name)
name: 唯一标识信号量。
返回值: 成功返回0,失败返回-1。
sem_wait() 等待一个信号量
#include <semaphore.h>
int sem_wait(sem_t* sem) //阻塞版本
int sem_trywait(sem_t* sem) //非阻塞版本
int sem_timewait(sem_t* sem,const struct timespec* abs_timeout) //超时返回版本
返回值: 成功返回0,失败返回-1。
sem_post() 发布一个信号量
#include <semaphore.h>
int sem_post(sem_t* sem)
返回值: 成功返回0,失败返回-1。
sem_getvalue() 获取当前信号量的值
#include <semaphore.h>
int sem_getvalue(sem_t* sem,int* sval)
sval 传出的信号量的值。
返回值: 成功返回0,失败返回-1。
sem_init() 初始化一个未命名信号量
#include <semaphore.h>
int sem_init(sem_t* sem,int pshared,unsigned int value)
pshared: 表明这个信号量是在线程间共享还是在进程间共享,如果pshared等于0,表示在单个进程的线程间共享,如果pshared不等于0,表示在进程间共享。
返回值: 成功返回0,失败返回-1。
sem_destroy() 销毁一个未命名信号量
#include <semaphore.h>
int sem_destroy(sem_t* sem)
返回值: 成功返回0,失败返回-1。
案例
多生产者和多消费者模型{也可以拆分成生产-消费者模型来看}。
1.生产者1将product1放入缓冲区后,消费者1才能取走product1
2.生产者2将product2放入缓冲区后,消费者2才能取走product2
3.只有缓冲区为空时,生产者1或生产者2才能讲product放入缓冲区
4.对缓冲区的访问要互斥的进行
unlink.c 删除创建的信号量
#include <semaphore.h>
#include <stdio.h>
int main(int argc,char** argv)
{
int i = 1;
int ret = 0;
for(; i < argc; ++i)
{
ret = sem_unlink(argv[i]);
if(ret < 0)
printf:("%s unlink error!\n",argv[i]);
}
return 0;
}
producer1.c 生产者1
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//创建信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
//缓冲区有几个product1
sem_t* semProduct1 = sem_open("SEMPRODUCT1",O_CREAT,0777,0);
//缓冲区可以放多少个product
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semBuf);
sem_wait(semMutex);
strcpy(mem,"product1");
printf("生产者1生产了product1\n");
sem_post(semMutex);
sem_post(semProduct1);
sleep(3);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
producer2.c 生产者2
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//创建信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
//缓冲区有几个product2
sem_t* semProduct2 = sem_open("SEMPRODUCT2",O_CREAT,0777,0);
//缓冲区可以放多少个product
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semBuf);
sem_wait(semMutex);
strcpy(mem,"product2");
printf("生产者2生产了product2\n");
sem_post(semMutex);
sem_post(semProduct2);
sleep(2);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
consumer1.c 消费者1
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//打开信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
sem_t* semProduct1 = sem_open("SEMPRODUCT1",O_CREAT,0777,0);
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semProduct1);
sem_wait(semMutex);
printf("消费者1取出了%s\n",mem);
strcpy(mem,"");
sem_post(semMutex);
sem_post(semBuf);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
consumer2.c 消费者2
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建共享内存映射区
char* mem = mmap(NULL,9,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
//打开信号量
//互斥访问缓冲区信号量
sem_t* semMutex = sem_open("SEMMUTEX",O_CREAT,0777,1);
sem_t* semProduct2 = sem_open("SEMPRODUCT2",O_CREAT,0777,0);
sem_t* semBuf = sem_open("SEMBUF",O_CREAT,0777,1);
while(1)
{
sem_wait(semProduct2);
sem_wait(semMutex);
printf("消费者2取出了%s\n",mem);
strcpy(mem,"");
sem_post(semMutex);
sem_post(semBuf);
}
//释放mmap
munmap(mem,9);
close(fd);
return 0;
}
先执行删除信号量代码,再执行生产消费者代码
./unlink SEMMUTEX SEMBUF SEMPRODUCT2 SEMPRODUCT1