一、进程之间的通信方式-----共享内存。
1.共享内存属于IPC对象。所以在使用共享内存之前也要申请key值与ID号
2.共享内存的作用机制以及范围 ?
作用机制:任意一个进程可以将数据放置到共享内存上,另外一个进程就可以将共享内存上的数据输出
作用范围:由于共享内存任意一个进程都可以看到,所以作用范围是linux下的任意两个进程。
二、关于共享内存的函数接口
1.申请key值。
key_t key=ftok(“.”,10);
2.申请共享内存的ID号 --shmget -------man 2 shmget
功能:
shmget - allocates a System V shared memory segment
//允许进程申请共享内存的空间
使用方法:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数:
key: key值
size :共享内存的总字节数们必须是PAGE_SIZE的倍数
shmflg: IPC_CREATE|0666 ---不存在则创建
返回值:
成功: 共享内存的ID号
失败: -1;
3.根据你的ID号去申请共享内存的区域 ------shmat()----man 2 shmat
使用方法
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid:共享内存的ID号
shmaddr:NULL 选择共享内存的地址,NULL代表系统来分配
shmflg: 设置为0 普通属性即可
返回值:
成功:共享内存的地址p
失败 : NULL
4.直接通过IO操作将数据放置到空间上
fgets(p, 1024,stdin);//从键盘获取数据
5.获取共享内存的数据
print(“%s\n”,p);
6.撤销映射 -----shmdt -------man 2 shmdt
使用方法
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:
shmaddr :共享内存的地址
返回值
成功 0;
失败 -1
7.删除共享内存 -----shmctl -----man 2 shmctl
使用方法
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid: 共享内存的ID号
cmd: IPC_RMID ---删除共享内存
buf: 如果是删除填NULL
返回值:
成功0
失败-1
三、信号量
共享内存一般很少自己单独使用,一般都是与信号量一起使用的。
1.什么是信号量
共享内存单独使用会出现数据践踏的情况,很明显是因为没有处理好互斥问题 ,如果信号量与共享内存连同,那么就可以解决共享内存的问题。有了信号量,就可以事项共享内存输入一次 ,输出一次。
2.关于信号量的函数接口
1).先申请key值
key_t key = ftok(".",10);
2)根据key值去申请信号量的ID号 ------semget ------ man 2 semget
函数功能:
semget - get a System V semaphore set identifier
//获取信号量的ID。
使用方法:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
参数:、
key ; key值
nsems 信号量中元素的个数 2
semflg: IPC_CREATE|0666 不存在就创建
返回值:
成功:信号量的ID号
失败 -1
3)如何初始化和删除信号量 ---- semctl ------ man 2 semctl
使用方法:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid;信号量的ID号
semnum:控制空间->0 控制数据->1
cmd: :IPC_RMID ----------删除信号量
SETVAL ----------初始化信号量的值
例如:
semcrl(semid,0,SETVAL,1);
semcrl(semid,1,SETVAL,0);
返回值
成功: 0
失败:-1
4)如何实现信号量的P/V操作 ---------semop ---------man 2 semop
P操作;1变0;
V操作: 0变1
使用方法:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
参数 :
semid:信号量的ID
sops:
struct sembuf{
unsigned short sem_num; /*需要操作的成员下标*/ 比如操作空间---0 操作数据 ----1
short sem_op; /* p操作/v操作 */ 比如p操作--- -1 v操作---- 1
short sem_flg; /* 普通属性 0*/
}
nsops:信号量操作结构体的个数 ----1
返回值:
成功: 0
失败:-1
共享内存实现通讯
client.c
#include "head.h"
int main(int argc,char *argv[])
{
//1. 申请key值。
key_t key = ftok(".",10);
key_t keyx = ftok(".",20);
//2. 根据key值去申请共享内存和信号量的ID号。
int shmid = shmget(key,1024,IPC_CREAT|0666);
int semid = semget(keyx,2,IPC_CREAT|0666);
//3. 根据id号去产生一块映射的空间。
char *p = shmat(shmid,NULL,0);
//4. 设置信号量的初始值。
semctl(semid,0,SETVAL,1); //有车位
semctl(semid,1,SETVAL,0); //没车
//空间p操作
struct sembuf space;
space.sem_num = 0; //空间
space.sem_op = -1; //p操作
space.sem_flg = 0; //普通属性
struct sembuf data;
data.sem_num = 1; //数据
data.sem_op = 1; //v操作
data.sem_flg = 0; //普通属性
//5. 不断阻塞地从键盘获取数据,并存放到共享内存中。
while(1)
{
//现在情况: 空间:1 数据:0
//请问空间能不能减1?
semop(semid,&space,1);
//现在情况: 空间:0 数据:0
//开车进去
fgets(p,1024,stdin);
//数据v操作
semop(semid,&data,1);
//现在情况: 空间:0 数据:1
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
return 0;
}
sever.c
#include "head.h"
int main(int argc,char *argv[])
{
//1. 申请key值。
key_t key = ftok(".",10);
key_t keyx = ftok(".",20);
//2. 根据key值去申请共享内存和信号量的ID号。
int shmid = shmget(key,1024,IPC_CREAT|0666);
int semid = semget(keyx,2,IPC_CREAT|0666);
//3. 根据id号去产生一块映射的空间。
char *p = shmat(shmid,NULL,0);
//4. 设置信号量的初始值。
semctl(semid,0,SETVAL,1); //有车位
semctl(semid,1,SETVAL,0); //没车
//空间v操作
struct sembuf space;
space.sem_num = 0; //空间
space.sem_op = 1; //v操作
space.sem_flg = 0; //普通属性
//数据p操作
struct sembuf data;
data.sem_num = 1; //数据
data.sem_op = -1; //p操作
data.sem_flg = 0; //普通属性
//4. 不断将共享内存的数据打印出来。
while(1)
{
//现在情况: 空间:0 数据:1
//请问数据能不能减1?
semop(semid,&data,1);
//现在情况: 空间:0 数据:0
//开车出去
printf("from shm:%s",p);
//空间v操作。
semop(semid,&space,1);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
//5. 撤销映射。
shmdt(p);
//6. 删除共享内存。
shmctl(shmid,IPC_RMID,NULL);
//7. 删除信号量。
semctl(semid,0,IPC_RMID);
return 0;
}
四、线程概念
1.进程与线程有什么区别:
线程是进程的内部资源
2.线程函数的特点
1)查看线程库中的函数接口 man 3 xxx
2)线程函数头文件 #include<pthread.h>
五、学习函数接口
1.如何创建一个子线程 --------pthread_create()----man 3 pthread_create()
使用方法:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:
thread: 存储线程ID号的变量的地址 变量的数据类型:pthread_t
attr: 线程的属性,普通属性设置为NULL
start_routine: 线程执行的例程函数,就是开启一个线程后,这个线程需要做什么东西。函数名长这样void *fun (void *arg)
arg:传递给例程函数的参数
返回值:
成功 0;
失败 非0;
#include "head.h"
void *fun(void *arg)
{
int a = *(int *)arg;
for (int i = 0; i < a; i++)
{
sleep(1);
printf("xiaole\n");
}
}
int main()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
int a = 10;
pthread_create(&tid, &attr, fun, (void *)&a);
sleep(2);
int ret;
ret = pthread_join(tid, NULL);
if (ret == 0)
{
printf("succ\n");
}
else
{
printf("!succ\n");
}
pthread_attr_destroy(&attr);
return 0;
}
注意:
Compile and link with -pthread.
//只要代码涉及到线程库,编译时就必须要链接线程库。
eg:gcc test.c -o test -lpthread
#include "head.h"
void *fun(void *a)
{
for (int i = 0; i < 5; i++)
{
sleep(1);
printf("321\n");
}
}
int main()
{
/*单进程*/
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
int i;
for (i = 0; i < 10; i++)
{
sleep(1);
printf("123\n");
}
return 0;
}
主线程 一旦结束,所有的子线程不论是否结束都会结束
2.如何接合一个线程(等待一个子进程的退出) -----》pthread_join()-----man 3 pthread_join
函数功能:
//接合一个结束的进程
使用方法:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数:
thread 需要接合的哪个线程的ID号
retval 存储的子线程的退出值的指针。如果设置为NULL,代表不关心子线程的退出状态
返回值:
成功: 0
失败:非0
#include "head.h"
int exit_state = 10;
void *fun(void *a)
{
for (int i = 0; i < 10; i++)
{
sleep(1);
printf("321\n");
}
pthread_exit((void *)(&exit_state));
}
int main()
{
/*单进程*/
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
int i;
for (i = 0; i < 5; i++)
{
sleep(1);
printf("123\n");
}
void *p = NULL;
pthread_join(tid, &p);
printf("%d\n", *(int *)p);
return 0;
}
3.如何退出一个线程,并将退出值返回给主线程 ————pthread_exit() -------man 3 pthread_exit
//函数功能:
pthread_exit - terminate calling thread
//结束一个正在运行的线程
使用方法:
#include <pthread.h>
void pthread_exit(void *retval);
参数:
retval :线程的退出值变量的地址
返回值
无
#include "head.h"
int exit_state=10;
void *fun(void *a)
{
for (int i = 0; i < 10; i++)
{
sleep(1);
printf("321\n");
}
pthread_exit((void*)(&exit_state));
}
int main()
{
/*单进程*/
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
int i;
for (i = 0; i < 5; i++)
{
sleep(1);
printf("123\n");
}
void*p=NULL;
pthread_join(tid,&p);
printf("%d\n",*(int *)p);
return 0;
}
4.导致线程退出的几种情况
①当线程调用pthread_exit()这个函数是,可以导致线程退出,并且可以携带一个退出值给这个进程中另外调用pthread_join()函数的线程
②当线程历程函数返回时,也可以导致线程的退出。这种情况等价于调用 int sig=10;
pthread_exit((void*)&sid) 等价于 return ((void*)&sid)
③如果进程被取消了,那么线程也会推出
④在进程中任意一个线程调用exit(),或者是主线程从main函数中返回。都代表进程退出
通过线程实现消息队列通信
jack.c
#include "head.h"
#define J2R 10
#define R2J 20
struct msgbuf{
long mtype;
char mtext[50];
};
void *snd_fun(void *arg) //arg = (void *)&msgid
{
int msgid = *(int *)arg;
struct msgbuf gec;
int ret;
while(1)
{
bzero(&gec,sizeof(gec));
gec.mtype = J2R;
fgets(gec.mtext,sizeof(gec.mtext),stdin);
ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
if(ret == -1)
{
printf("msgsnd error!\n");
exit(-1);
}
if(strncmp(gec.mtext,"quit",4) == 0)
{
exit(0); //进程退出,可以导致所有的线程都退出。
}
}
}
void *rcv_fun(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf gec;
int ret;
while(1)
{
bzero(&gec,sizeof(gec));
ret = msgrcv(msgid,&gec,sizeof(gec.mtext),R2J,0);
if(ret == -1)
{
printf("msgrcv error!\n");
exit(-1);
}
printf("from Rose:%s",gec.mtext);
if(strncmp(gec.mtext,"quit",4) == 0)
{
msgctl(msgid,IPC_RMID,NULL);
exit(0);
}
}
}
int main(int argc,char *argv[])
{
//1. 申请消息队列的key值与ID号。
key_t key = ftok(".",10);
int msgid = msgget(key,IPC_CREAT|0666);
//2. 创建两个子线程。
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,snd_fun,(void *)&msgid);
pthread_create(&tid2,NULL,rcv_fun,(void *)&msgid);
//3. 接合线程。
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
rose.c
#include "head.h"
#define J2R 10
#define R2J 20
struct msgbuf{
long mtype;
char mtext[50];
};
void *snd_fun(void *arg) //arg = (void *)&msgid
{
int msgid = *(int *)arg;
struct msgbuf gec;
int ret;
while(1)
{
bzero(&gec,sizeof(gec));
gec.mtype = R2J;
fgets(gec.mtext,sizeof(gec.mtext),stdin);
ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
if(ret == -1)
{
printf("msgsnd error!\n");
exit(-1);
}
if(strncmp(gec.mtext,"quit",4) == 0)
{
exit(0); //进程退出,可以导致所有的线程都退出。
}
}
}
void *rcv_fun(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf gec;
int ret;
while(1)
{
bzero(&gec,sizeof(gec));
ret = msgrcv(msgid,&gec,sizeof(gec.mtext),J2R,0);
if(ret == -1)
{
printf("msgrcv error!\n");
exit(-1);
}
printf("from Jack:%s",gec.mtext);
if(strncmp(gec.mtext,"quit",4) == 0)
{
msgctl(msgid,IPC_RMID,NULL);
exit(0);
}
}
}
int main(int argc,char *argv[])
{
//1. 申请消息队列的key值与ID号。
key_t key = ftok(".",10);
int msgid = msgget(key,IPC_CREAT|0666);
//2. 创建两个子线程。
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,snd_fun,(void *)&msgid);
pthread_create(&tid2,NULL,rcv_fun,(void *)&msgid);
//3. 接合线程。
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
六、线程的分离属性
1.什么是分离属性:
分离属性是线程的一个属性,有了分离属性的线程,是不需要别的线程来接合自己的。
虽然说是分离的,但是进程退出时,该线程还是要退出
分离属性线程 -------不需要调用pthread_join去接合的
非分离属性线程 -------需要调用pthread_join去接合的 ----默认创建
2.如何创建分离属性的进程?
方法一:添加一个分离属性到一个属性变量中,在使用这个属性变量去创建一个线程,创建出来的线程就具有分离属性
1)定义一个属性变量 ---------》数据类型 pthread_attr_t
pthread_attr_t attr;
2)初始化属性变量 - -------pthread_attr_init --- man 3 pthread_attr_init
功能:
initialize thread attributes object
//初始化线程属性变量
使用方法:
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
参数:
attr:属性变量的地址
返回值:
成功0;
失败非0
3)添加分离属性到书信变量中 ------- pthread_attr_setdetachstate----man 3 pthread_attr_setdetachstate
功能:
set detach state attribute in thread attributes object
//添加分离属性到书信变量中
使用方法:
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:
attr:属性变量的地址
detachstate:
PTHREAD_CREATE_DETACHED -----分离属性
PTHREAD_CREATE_JOINABLE -----------非分离属性
返回值:
成功 0
失败 非0
4)利用这个属性变量去创建一个线程
pthread_create(&tid,&attr,fun,NULL); --> 这样创建出来的线程就是分离属性的线程,不需要别人去join它的。
5)销毁属性变量 -------pthread_attr_destroy--------man 3 pthread_attr_destroy
使用方法:
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);
参数:
attr:属性变量的地址
返回值
成功 0
失败 非0
代码 查看分离属性的线程是否会被接合
#include "head.h"
void *fun(void *arg)
{
int a = *(int *)arg;
for (int i = 0; i < a; i++)
{
sleep(1);
printf("xiaole\n");
}
}
int main()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
int a = 10;
pthread_create(&tid, &attr, fun, (void *)&a);
sleep(2);
int ret;
ret = pthread_join(tid, NULL);
if (ret == 0)
{
printf("succ\n");
}
else
{
printf("!succ\n");
}
pthread_attr_destroy(&attr);
return 0;
}
方法二: 先创建一个普通的线程、然后再普通的线程中去调用设置分离属性的函数,呢这个线程就变成分离属性的线程了。
1)设置线程本身为分离属性-------pthread_deatch--------man 3 pthread_detach
功能:
pthread_detach - detach a thread
//分离一个线程
使用方法:
#include <pthread.h>
int pthread_detach(pthread_t thread);
参数
thread: 线程的ID号
返回值
成功 0
失败 非0
2)再线程内部获取自己的ID号 pthread_self ---------man 3 pthread_self
功能:
pthread_self - obtain ID of the calling thread
//获取线程的ID
使用方法:
#include <pthread.h>
pthread_t pthread_self(void);
参数 :无
返回值:
成功: 线程的ID
失败:不会失败
#include "head.h"
void *fun(void *arg)
{
pthread_t tid = pthread_self();
pthread_detach(tid);
int a = *(int *)arg;
for (int i = 0; i < a; i++)
{
sleep(1);
printf("xiaole\n");
}
}
int main()
{
pthread_t tid;
int a = 10;
pthread_create(&tid, NULL, fun, (void *)&a);
sleep(2);
int ret;
ret = pthread_join(tid, NULL);
if (ret == 0)
{
printf("succ\n");
}
else
{
printf("!succ\n");
}
return 0;
}
七、线程的取消
1、什么是线程的取消?
通过发送一个取消请求给线程,那么这个线程收到请求之后,就会提前退出。
2、如何发送? -> pthread_cancel() -> man 3 pthread_cancel
函数功能:
send a cancellation request to a thread
使用格式:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数:
thread: 你想取消的那个线程的ID号。
返回值:
成功:0
失败:非0
3.设置线程取消相应的状态 ---------pthread_setcancelstate -------man 3 pthread_setcancelstate
功能:
pthread_setcancelstate, pthread_setcanceltype - set cancelability state
and type
使用方法:
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
参数:
state
PTHREAD_CANCEL_ENABLE ====能响应
PTHREAD_CANCEL_DISABLE ---------不能响应
oldstate :保留之前状态的指针,如果不想保留则设置为NULL
返回值
成功 0
失败 非0
注意:
If a cancellation request is received, it is blocked until cancelability is enabled.
//如果再不能响应取消的的情况下,收到一个取消请求,那么就会一直阻塞到这个线程能响应取消为止、
#include "head.h"
void *fun(void *arg)
{
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
for (int i = 0; i < 10; i++)
{
sleep(1);
printf("xiao le\n");
}
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
sleep(5);
pthread_cancel(tid);
return 0;
}
4.设置线程的取消类型 ----pthread_setcancel ------man 3 pthread_setcanceltype
功能:
pthread_setcancelstate, pthread_setcanceltype - set cancelability state
and type
使用方法:
#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);
参数:保留之前状态的指针,如果不想保留则设置为NULL
返回值
成功 0
失败 非0
type:
PTHREAD_CANCEL_DEFERRED ------延迟响应 (默认)
PTHREAD_CANCEL_ASYNCHRONOUS -----------立即响应
oldtype:
1)当线程下一行代码是取消点函数时,就会响应
2)取消点函数有哪些 ------ man 7 pthreads
#include "head.h"
void *fun(void *arg)
{
int i,j;
for(i=0;i<100000;i++)
{
for(j=0;j<100000;j++)
{
//在此期间,都没有遇到取消点函数。
}
}
while(1)
{
fputc('a',stderr); //取消点 //输出一个字符到屏幕上。
printf("helloworld!\n"); //取消点
}
}
int main(int argc,char *argv[])
{
//1. 创建线程。
pthread_t tid;
pthread_create(&tid,NULL,fun,NULL);
//2. 马上发送取消请求。
pthread_cancel(tid);
printf("我已经发送了取消请求给你了!\n");
//3. 接合。
pthread_join(tid,NULL);
return 0;
}
八.线程的取消例程函数
1.什么是线程的取消例程函数?
当线程收到取消请求时,先不要马上响应取消,而是先执行一个例程函数,执完这个例程函数之后再推出。
2.为什么要使用线程取消例程函数?
因为在一个进程中,有可能由多个线程同时使用一个个资源,如果某一个线程在使用资源时,因为被取消而提前退出,那么这个线程就会带着这个资源而退出,导致其他线程无法继续使用这个线程、
解决方案: 先告诉线程,将来在收到请求时,先释放资源,再响应取消,那么这么做,就可以避免这些线程带着资源退出。
3.如何实现?
1)如何在线程历程中说明线程的取消例程函数是哪个 ----pthread_cleanup_push --------man 3 pthread_cleanup_push
使用方法
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
参数:
routine: 线程的取消例程函数。必须长 void fun(void *arg)
arg : 传递给线程的取消例程函数的参数
返回值:无
信号处理函数: void fun (int sig)
线程例程函数: void *fun(void *arg)
线程取消例程函数: void fun(void *arg)
2)删除 取消例程函数。------》pthread_cleanup_pop ----------man 3 pthread_cleanup_pop
使用方法:
#include <pthread.h>
void pthread_cleanup_pop(int execute);
参数:
execute :非0 ---------》在删除时,先执行一遍线程的取消例程函数,再删除、
0 ---------在删除时,会马上删除
返回值 : 无
#include "head.h"
void myfun(void*arg){
printf("hellow\n");
}
void *fun(void *arg)
{
pthread_cleanup_push(myfun,NULL);
int i,j;
for(i=0;i<5;i++)
{
sleep(1);
printf("helloworld!\n"); //取消点
}
pthread_cleanup_pop(1);
}
int main(int argc,char *argv[])
{
//1. 创建线程。
pthread_t tid;
pthread_create(&tid,NULL,fun,NULL);
//2. 马上发送取消请求
sleep(2);
pthread_cancel(tid);
printf("我已经发送了取消请求给你了!\n");
//3. 接合。
pthread_join(tid,NULL);
return 0;
}
注意
1.取消历程函数是会被压倒栈中,需要删除
2.pthread_cleanup_pop和pthread_cleanup_push是配合一起使用的。
九、同步互斥方式 ---------有名信号量
1.在进程与线程中,同步互斥的方式有哪些
1)信号量 ------------处理进程之间的互斥
2)有名信号量------------处理进程之间的互斥
3)无名信号量------------处理线程之间的互斥
4)互斥锁------------处理线程之间的互斥
5)读写锁------------处理线程之间的互斥
2.什么是同步互斥?为什么要进行同步互斥?
同步互斥就是为了线程/进程处理任务时有先后顺序,为了防止线程/进程抢占资源。
3.有名信号量的函数接口?
1)创建并打开一个有名信号量, -------sem_open -------man 3 sem_open
函数功能:
sem_open - initialize and open a named semaphore
//初始化并打开一个有名信号量
使用方法:
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag); 打开一个存在的有名信号量
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); 创建并打开一个不存在的有名信号量
参数:
name :要求必须以”/“作为开头 例如“/sem_test” ---------最终会在/dev/shm目录下创建
oflag:O_CREAT ----不存在就会创建,存在就会忽略
O_CREAT | O_EXCL -------不存在就会创建,存在就会出错
mode : 八进制权限 eg:0777
value : 有名信号量的起始值
返回值:
成功:有名信号量的地址 sem_t *
失败:NULL
注意:
如果在oflag中有O_CREATE这个选项,那么剩余两个参数mode和value必须要填
如果有名信号量已经存在了,但是你有写了O_CREATE,那么后面的两个参数就算你填了也会忽略。
//虽然这个函数不是以pthread开头,但是编译时,也要链接线程库
3)如何关闭有名信号量 ---------sem_close -----man 3 sem_close
功能:
sem_close - close a named semaphore
//关闭有名信号量
使用方法:
#include <semaphore.h>
int sem_close(sem_t *sem);
Link with -pthread.
参数:
sem:有名信号量的地址
返回值
成功 0
失败-1
关闭信号的意思是:只是关闭而已。下次使用直接打开,不用去创建
3)如何删除有名信号量 -------sem_unlink ---------man 3 sem_unlink
功能
sem_unlink - remove a named semaphore
/./删除有名信号量
使用方法:
#include <semaphore.h>
int sem_unlink(const char *name);
Link with -pthread.
参数:
name:有名信号量的名字
返回值
成功 0
失败-1
4)有名信号量的p操作 sem_wait -------man 3 sem_wait
功能:
sem_wait, sem_timedwait, sem_trywait - lock a semaphore
使用方法:
#include <semaphore.h>
int sem_wait(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功: 0
失败-1
如果当前值为2 ,那么调用sem_wait就会马上返回,并且将值设置为1
如果当前值为1 ,那么调用sem_wait就会马上返回,并且将值设置为0
如果当前值为0 ,那么调用sem_wait就会阻塞,要等到值变为1为止,才可以减一
5)又名信号量的V操作 -------- sem_post ---------man 3 sem_post
sem_post - unlock a semaphore
使用方法:
#include <semaphore.h>
int sem_post(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功: 0
失败-1
如果当前值为0,那么调用sem_post就会马上返回,并且将值设置为1
如果当前值为1 ,那么调用sem_post就会马上返回,并且将值设置为2
如果当前值为2 ,那么调用sem_post就会马上返回,并且将值设置为3
…………