ftok

通过文件路径 pathname 以及 proj_id 获得键值key(键值:信号灯集的标识),只要文件路径和proj_id不变,key值就不变

头文件:
    #include <sys/types.h>
    #include <sys/ipc.h>
原型:
    key_t ftok(const char *pathname, int proj_id);
参数:
    char *pathname:文件路径(目录文件或是普通文件);
    int proj_id:非0参数,用户自定义;
返回值:
    成功,返回key值;
    失败,返回-1,更新errno;
例程:
    key_t key = ftok("./", 'a');
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %x\n", key);

semget

创建或打开信号灯集对象,创建成功后信号灯集的所有信号量的值默认初始化为0

头文件: 
    #include <sys/types.h> 
    #include <sys/ipc.h> 
    #include <sys/sem.h> 
原型: 
    int semget(key_t key, int nsems, int semflg);
参数:
    key_t key:键值,提前约定或ftok函数创建获得;
    int nsems:信号灯集对象中包含的信号量个数;
    int semflg:设置信号灯集的访问权限,参数如下:
        IPC_CREAT:若信号灯集对象不存在则创建,否则打开已经存在的信号灯集对象;
        IPC_EXCL:若信号灯集对象不存在则创建,否则返回错误;
        IPC_NOWAIT:(非阻塞)如果本操作需要等待,则直接返回错误;
            IPC_CREAT|0666     如果内核中不存在与key值相同的信号灯集,则创建信号灯集,并设置8进制权限(按位或)。如果存在,则直接返回信号灯集标识符semid;
返回值:
    成功,返回信号灯集标识 semid;
    失败,返回-1,更新errno;
例程:
    int semid = semget(key, 2, IPC_CREAT|0777);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid = %d\n", semid);

semop

对信号灯集中指定信号量的PV操作:申请信号量P(-1)|释放信号量V(+1)

头文件: 
    #include <sys/types.h> 
    #include <sys/ipc.h> 
    #include <sys/sem.h>
原型:
    int semop(int semid, struct sembuf *sops, unsigned nsops);
参数:
    int semid:信号灯集对象id;
    struct sembuf *sops:指向sembuf结构体的指针,操作信号灯集中的信号量
        struct sembuf
        {
            unsigned short int sem_num;   /* semaphore number */信号灯编号,从0开始编
            short int sem_op;     /* semaphore operation */
                正数,例如+1或+n,释放信号量(V操作),增加信号量的值
                负数,例如-1或-n,申请信号量(P操作),减小信号量的值
                0,阻塞,直到信号量的值为0
            short int sem_flg;   /* operation flag */操作控制标志
                0:以阻塞方式运行
                IPC_NOWAIT:当指定的PV操作不能完成时,进程不会被阻塞,立即返回-1,errno设置为EAGAIN;
                SEM_UNDO:以阻塞方式运行;进程异常退出时,执行信号量解除操作;例如:进程执行了P操作后异常退出,如果sem_flg设置了SEM_UNDO,则内核会对该进程执行V操作,保证安全性;
        };
    unsigned nsops:操作结构体struct sembuf对象的数量;
返回值:
    成功,返回0;
    失败,返回-1,更新errno;
例程:
    struct sembuf sops;//定义结构体struct sembuf对象
    sops.sem_num = 0;//操作0号灯
    sops.sem_op = -1;//申请信号量
    sops.sem_flg = 0;//以阻塞方式运行
    if(semop(semid, &sops, 1) < 0)
    {
        perror("semop");
        return -1;
    }
    printf("P操作成功 semval:1\n");

semctl

信号灯集的控制(信号量初始化、信号量值操作、信号灯集删除操作)

头文件: 
    #include <sys/types.h> 
    #include <sys/ipc.h> 
    #include <sys/sem.h>
原型:
    int semctl(int semid, int semnum, int cmd, ...);
参数:
    int semid:信号灯集对象id;
    int semnum:指定要控制的信号灯的编号;(如果控制是针对整个信号灯集,该值设为0)
    int cmd:要执行的控制命令:
        IPC_RMID:删除信号灯集,第四个参数不填。第二个参数,随便填int类型数据,设置为0;
        IPC_STAT:获取信号灯集的属性(struct ipc_perm数据项),存放在第四个参数(struct semid_ds中的sem_perm数据项中);
        IPC_SET:设置信号灯集的属性(struct ipc_perm数据项),存放在第四个参数(struct semid_ds中的sem_perm数据项中);
        GETVAL:获取指定信号灯的值,第四个参数不填;
        SETVAL:设置指定信号灯的值;这个值通过union semun中的val成员来指定;
        GETALL:获取信号灯集中所有信号灯的值,存储在第四个参数中。第二个参数,随便填int类型数据,设置为0;
        SETALL:设置信号灯集中所有信号灯的值,存储在第四个参数中(一般用于信号量初始化设置初始值)。第二个参数,随便填int类型数据,设置为0;
        GETPID:获取信号灯拥有者进程的PID值;
    ...:不定参数:不同的cmd,该参数不同;与控制命令cmd配合的参数如下:
        union semun {
            int              val;    /* Value for SETVAL */
            struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
            unsigned short  *array;  /* Array for GETALL, SETALL */
            struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
        };
返回值:
    成功,返回值大于或等于0;
    失败,返回-1,更新errno; 

例程:

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <string.h>
int main(int argc, const char *argv[])
{
    //申请key值
    key_t key = ftok("./", 'c');
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %x\n", key);
    //创建信号灯集
    int semid = semget(key, 2, IPC_CREAT|0777);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    printf("semid = %d\n", semid);
    //SETALL 设置所有信号灯的值 
    unsigned short setall[2] = {2, 1};
    if(semctl(semid, 0, SETALL, setall) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("0:%d 1:%d\n", setall[0],setall[1]);
    //GETALL 获取所有信号灯的值
    unsigned short getall[2];
    if(semctl(semid, 0, GETALL, getall) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("0:%d 1:%d\n", getall[0],getall[1]);
    //SETVAL 设置单个信号灯的值
    int setval = 5;
    if(semctl(semid, 1, SETVAL, setval) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("设置成功\n");
    //GETVAL 获取单个信号灯的值
    int getval = semctl(semid, 1, GETVAL);
    printf("getval=%d\n", getval);
    //IPC_RMID 删除信号灯
    if(semctl(semid, 0, IPC_RMID) < 0)
    {
        perror("semctl");
        return -1;
    }
    printf("删除信号灯成功\n");
}

结果:

key = 63190789
semid = 0
0:2 1:1
0:2 1:1
设置成功
getval=5
删除信号灯成功

应用例程

创建两个没有亲缘关系的进程,一个进程倒置字符串,一个进程打印字符串。倒置一次打印一次。提示,使用共享内存,及信号灯集, 信号(pause)。

打印进程

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>   
int main(int argc, const char *argv[])
{
    //创建有名管道
    if(mkfifo("./myfifo", 0664) < 0)
    {
        if(errno != EEXIST)
        {
            perror("mkfifo");
            return -1;
        }
    }
    //只读打开有名管道
    int fd = open("./myfifo", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }
    //读取管道中倒置进程的pid
    pid_t pid;
    if(read(fd, &pid, sizeof(pid)) < 0)//倒置进程若没有启动,read阻塞
    {
        perror("read");
        return -1;
    }
    printf("pid = %d\n", pid);
    close(fd);
    /***初始化共享内存及信号灯集合*****/ 
    //共享内存
    //1.key
    key_t key = ftok("./", 'e');
    if(key<0)
    {
        perror("ftok");
        return -1;
    }
    //2.创建共享内存
    int shmid = shmget(key, 128, IPC_CREAT|0777);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    //3.映射
    void* shmaddr = shmat(shmid, NULL, 0);
    if((void*)-1 == shmaddr)
    {
        perror("shmat");
        return -1;
    }
    //str即为共享内存空间的首地址
    char* str = (char*)shmaddr;
    //4.写入共享内存
    strcpy((char*)shmaddr, "AAAAA|BBBBB");
    //信号灯集合
    //1.key 不同的通信机制可共用一个key
    //2.创建信号灯集
    int semid = semget(key, 2, IPC_CREAT|0777);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    //3.设置信号灯的值
    //0号灯控制打印,1号灯控制倒置
    //初始0号灯非阻塞,1号灯阻塞
    unsigned short setall[2] = {1, 0};
    if(semctl(semid, 0, SETALL, setall) < 0)
    {
        perror("semctl");
        return -1;
    }
    //唤醒倒置进程 
    if(kill(pid, 10) < 0)
    {
        perror("kill");
        return -1;
    }
    //0号灯控制打印,1号灯控制倒置
    struct sembuf sops;
    while(1)
    {
        //0号灯的P操作
        sops.sem_num = 0;     //0号灯 
        sops.sem_op = -1;     //P操作
        sops.sem_flg = 0;    //阻塞方式运行
        if(semop(semid, &sops, 1)<0)
        {
            perror("semop");
            return -1;
        }
        printf("%s\n", str);
         sleep(1);
        //1号灯的V操作
        sops.sem_num = 1;     //1号灯 
        sops.sem_op = +1;     //V操作
        sops.sem_flg = 0;
        if(semop(semid, &sops, 1)<0)
        {
            perror("semop");
            return -1;
        }
    }
    return 0;
}

倒置进程

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>  
typedef void (*sighandler_t)(int);
void handler(int sig){}

int main(int argc, const char *argv[])
{
    //注册信号处理函数
    sighandler_t s = signal(10, handler);
    if(SIG_ERR == s)
    {
        perror("signal");
        return -1;
    }
    //创建有名管道
    if(mkfifo("./myfifo", 0664) < 0)
    {
        if(errno != EEXIST)
        {
            perror("mkfifo");
            return -1;
        }
    }
    //只写打开有名管道
    int fd = open("./myfifo", O_WRONLY);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }
    //向管道中写入倒置进程的pid
    pid_t pid = getpid();
    printf("pid=%d\n", pid);
    if(write(fd, &pid, sizeof(pid)) < 0)
    {
        perror("write");
        return -1;
    }
    close(fd);
    //阻塞,等待打印进程唤醒信号(信号10)
    pause();
    //共享内存
    //1.key
    key_t key = ftok("./", 'e');
    if(key<0)
    {
        perror("ftok");
        return -1;
    }
    //2.打开共享内存
    int shmid = shmget(key, 128, IPC_CREAT|0777);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    //3.映射
    void* shmaddr = shmat(shmid, NULL, 0);
    if((void*)-1 == shmaddr)
    {
        perror("shmat");
        return -1;
    }
    //读取共享内存数据 str即为共享内存空间的首地址
    char* str = (char*)shmaddr;
    //信号灯集合
    //1.key 不同的通信机制可共用一个key
    //2.打开信号灯集
    int semid = semget(key, 2, IPC_CREAT|0777);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    //0号灯控制打印,1号灯控制倒置
    struct sembuf sops;
    int size = strlen(str);
    while(1)
    {
        //1号灯的P操作
        sops.sem_num = 1;     //1号灯 
        sops.sem_op = -1;     //P操作
        sops.sem_flg = 0;    //阻塞方式运行
        if(semop(semid, &sops, 1)<0)
        {
            perror("semop");
            return -1;
        }
        int i = 0;
        for(i=0; i<size/2; i++)
        {
            char temp = str[i];
            str[i] = str[size-1-i];
            str[size-1-i] = temp;
        }
        //0号灯的V操作
        sops.sem_num = 0;     //0号灯 
        sops.sem_op = +1;     //V操作
        sops.sem_flg = 0;
        if(semop(semid, &sops, 1)<0)
        {
            perror("semop");
            return -1;
        }
    }
    return 0;
}

结果

Last modification:2021 年 04 月 07 日 23 : 14 : 46