进击吧,linux(十六) 信号量同步编程
0赞
Linux进程和进程之间有多种通讯方式。Linux进程间通讯的主要方式有:
1、 无名管道
2、 有名管道
3、 信号
4、 消息队列
5、 共享内存
6、 信号量
7、 套接字
信号通讯包括有信号量同步。信号量同步用在多个进程间执行顺序是有明确先后的地方。比如对于两个进程AB,B进程必须要等待A完成后,才能执行自己的程序。这个时候,就可以用到信号量的同步。
信号量编程和上次的信号量互斥编程所用的函数是一致的。
8.1.1 函数名
semget
8.1.2 函数原形
int semget(key_t key, int nsems, int semflg)
8.1.3 函数功能
获取一个信号量的集合标识符,当key指定的信号量不存在的时候并且semflg包含了IPC_CREAT,就会创建一个信号量集合。
8.1.4 所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.1.5 返回值
成功: 信号量集合标识符
失败: -1
8.1.6 参数说明
key: 打开的信号量的键值,使用ftok(char *,int )创建的键值
nsems:指定信号量集合包含的信号量个数
semflg:标志
-IPC_CREAT:KEY所关联的信号量不存在,就创建信号量
8.2.1 函数名
semop
8.2.2 函数原形
int semop(int semid, struct sembuf *sops, unsigned nsops)
8.2.3 函数功能
对信号量操作,申请或者释放信号量
8.2.4 所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.2.5 返回值
成功: 0
失败: -1
8.2.6 参数说明
semid: 要操作的信号量集合的标识符
sops: 信号量操作的数组,每一个数组元素对应信号量的操作
struct sembuf{
unsigned short sem_num; /*semaphore number*/
short sem_op; /*semaphore operation*/
short sem_flg; /*operation flags*/
}
sem_num: 对信号量集合里面的哪些信号操作
sem_op: 信号量的操作,正数表示释放信号量,负数表示请求信号量,如果没有请求到信号量,那么进程就阻塞,等到有信号量为止。
sem_flg:
-SEM_UNDO:当信号操作失败,系统会自动的释放掉信号量
-SEM_NOWAIT:当信号操作造成进程阻塞,不阻塞程序,继续执行
nsops: 要操作多少个信号量
8.3 控制信号量
8.3.1 函数名
semctl
8.3.2 函数原型
int semctl(int semid, int semnum, int cmd, …)
8.3.3 函数功能
由cmd决定对信号量集合的操作
8.3.4所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.3.5返回值
失败:-1
成功:根据不同的命令返回不同的值
GETVAL: 返回信号量的值
8.3.6 参数说明
semid:要操作的信号量集合的标识符
semnum: 命令的参数
可以看出,这个参数就是一个联合体。4个字节大小。根据不同的命令,填入不同的值。
cmd: 命令
SETVAL: 设置信号量的值
GETVAL: 获取信号量的值
…: 可选参数,根据命令,有不同的参数
以下就写程序来学习一下怎么使用信号量互斥。这里,假设有两个进程AB,A进程产生一个文件,B进程要等A进程产生完毕后,将该文件重命名。
#include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<sys/ipc.h> #include<sys/sem.h> void main() { int fd; key_t key; int semid; int n; struct sembuf sops; char *p = "hello weiqi7777 "; /*创建键值*/ key = ftok("/home",5); /*创建并打开信号量*/ semid = semget(key,1,IPC_CREAT); /*设置信号量为0,用于同步使用*/ semctl(semid,0,SETVAL,0); /*读取信号量的值*/ n = semctl(semid,0,GETVAL); printf("%d\n",n); /*创建文件*/ fd = creat("file.txt",0775); /*休息*/ sleep(10); /*往文件写入内容*/ write(fd,p,strlen(p)); /*关闭文件*/ close(fd); /*释放信号量*/ sops.sem_num = 0; sops.sem_op = 1; sops.sem_flg = SEM_UNDO; semop(semid,&sops,1); }
在程序中,首先使用ftok创建了一个键值。使用的第一个参数是/home目录,第二个是随意一个数字。
然后创建一个信号量集合。信号量集合包含了1个信号量。信号量对应struct sembuf sops。
semctl,设置信号量的值为0,因为是要信号量同步,所以要设置为0,这样,即使B进程先执行,也获取不了信号量,就会阻塞。
创建文件,然后延时,然后向文件中写入一些信息,最后释放信号量,这个时候信号量的值就是1了。这样B进程就可以获取到信号量,就可以执行程序了。
然后是B进程:
#include<stdlib.h> #include<sys/ipc.h> #include<sys/sem.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> void main() { key_t key; int semid; int n; int ret; struct sembuf sops; /*创建键值*/ key = ftok("/home",5); /*创建并打开信号量*/ semid = semget(key,1,IPC_CREAT); /*读取信号量的值*/ n = semctl(semid,0,GETVAL); printf("%d\n",n); /*获取信号量*/ sops.sem_num = 0; sops.sem_op = -1; sops.sem_flg = SEM_UNDO; ret = semop(semid,&sops,1); printf("%d\n",ret); /*取走产生文件*/ system("mv file.txt file_new.txt"); }
对于B进程,要注意的是,创建的键值要和A进程创建的进程一致,这样,两个进程才会关联到同一个信号量集合,才能对信号量集合进行一致操作。
B进程获取信号量,因为A进程设置信号量的值是0,所以B进程就会获取失败,然后B进程就会被阻塞掉。直到能获取到信号量为止。当A进程释放信号量后,B进程就获取到信号量了,然后执行自己的程序。
程序执行效果:
A,B进程都运行。A进程创建一个文件,然后休眠10s。在这过程中,B进程获取不了信号量,所以阻塞。
当A执行完毕后,释放了信号量,B进程获取了信号量,所以程序继续执行,将A进程创建的文件给重命名了。
看起来,信号量同步和信号量互斥是很像了。只是区别在于信号量的设置初值不一样,对于信号量互斥,初值设置为非0。这样,各个进程申请信号量的机会是一样的,在这种情况下,进程间执行的顺序是没有规定的。
而对于信号量同步,信号量初值设置为0。这样,各个进程就都不能申请信号量了,而要等其他的进程释放信号量后才行了。这样,进程间就有执行的顺序了,这样就会出现,某些进程必须要在某些进程之后执行,这样就实现进程间同步执行了。
另外,在信号量互斥中,进程使用完信号量后,要释放信号量,这样其他进程才能使用该信号量。而在信号量同步中,先执行的进程只释放信号量,后执行的进程只申请信号量,这样才能实现,先执行的进程先执行,后执行的进程后执行。