weiqi7777

进击吧,linux(十六) 信号量同步编程

0
阅读(2567)

 

Linux进程和进程之间有多种通讯方式。Linux进程间通讯的主要方式有:

1、  无名管道

2、  有名管道

3、  信号

4、  消息队列

5、  共享内存

6、  信号量

7、  套接字

信号通讯包括有信号量同步。信号量同步用在多个进程间执行顺序是有明确先后的地方。比如对于两个进程ABB进程必须要等待A完成后,才能执行自己的程序。这个时候,就可以用到信号量的同步。

信号量编程和上次的信号量互斥编程所用的函数是一致的。

8.1 创建/打开信号量集合

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 操作信号量

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 命令的参数

clip_image002

可以看出,这个参数就是一个联合体。4个字节大小。根据不同的命令,填入不同的值。

cmd 命令

SETVAL:   设置信号量的值

GETVAL:       获取信号量的值

可选参数,根据命令,有不同的参数

以下就写程序来学习一下怎么使用信号量互斥。这里,假设有两个进程ABA进程产生一个文件,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进程就获取到信号量了,然后执行自己的程序。

程序执行效果:

clip_image004

A,B进程都运行。A进程创建一个文件,然后休眠10s。在这过程中,B进程获取不了信号量,所以阻塞。

clip_image006

A执行完毕后,释放了信号量,B进程获取了信号量,所以程序继续执行,将A进程创建的文件给重命名了。

看起来,信号量同步和信号量互斥是很像了。只是区别在于信号量的设置初值不一样,对于信号量互斥,初值设置为非0。这样,各个进程申请信号量的机会是一样的,在这种情况下,进程间执行的顺序是没有规定的。

而对于信号量同步,信号量初值设置为0。这样,各个进程就都不能申请信号量了,而要等其他的进程释放信号量后才行了。这样,进程间就有执行的顺序了,这样就会出现,某些进程必须要在某些进程之后执行,这样就实现进程间同步执行了。

另外,在信号量互斥中,进程使用完信号量后,要释放信号量,这样其他进程才能使用该信号量。而在信号量同步中,先执行的进程只释放信号量,后执行的进程只申请信号量,这样才能实现,先执行的进程先执行,后执行的进程后执行。