10.Linux网络编程-system v信号量

一:信号量简介
信号量本质就是一个计数器,用于统计临界资源数目的计数器,信号量是用来调协进程对共享资源的互斥访问,采用PV原语的原理。它的P(sv)和V(sv)行为是这样的:
P(sv):要访问受保护的资源的进程或者线程试图对信号量的值减1,如果信号量的值现在不可用,即信号量为0,减1操作将被阻塞(休眠),直到信号量大于0时,才会得以继续执行;
V(sv):要释放受保护的资源时,信号量的值将会加1,此时信号量的值大于0,其他请求该资源被信号量阻塞的线程或者进程将被唤醒;
总之,信号量为0(信号量不能小于0),无可用资源,信号量大于0,有可用资源。

二:函数简介

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

@semget:新建信号量
@int :成功返回相应的信号量标识符,失败返回-1,errno的值如下:
	EACCESS:没有权限
	EEXIST:信号量集已经存在,无法创建
	EIDRM:信号量集已经删除
	ENOENT:信号量集不存在,同时semflg没有设置IPC_CREAT标志
	ENOMEM:没有足够的内存创建新的信号量集
	ENOSPC:超出限制
@key:整数或者IPC_PRIVATE(0),信号量的标识。
	IPC_PRIVATE,表示私有的,意味信号量只能用于亲缘进程通信
@nsems:信号量集的个数,换句话来说,semget创建的信号量集id,可以控制nsems个资源的访问和释放
@semflg:九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的;
如果单独使用IPC_CREAT,则shmget()要么返回一个新创建的信号量的标识符,要么返回具有相同关键字值的标识符;
如果IPC_EXCL和IPC_CREAT一起使用,如果信号量已经存在则返回一个失败值-1,否则创建一个新的信号量,IPC_EXCL单独使用是没有用处的。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);

@semop:实现信号量的资源操作(P和V)
@int :成功返回相应的信号量标识符,失败返回-1
@semid:信号量集标识符
@sops:指向进行操作的信号量集结构体数组的首地址
	struct sembuf
	{
	     unsigned short sem_num;  /* semaphore number */
	     short sem_op;/* semaphore operation */
	     short sem_flg;/* operation flags */
	}
	sem_op:它的值可为正数、负数、0。正数表示释放资源操作(V操作),负数表示要请求资源访问的操作(P操作),0表示等待可用资源。
	sem_flg:它的值可为0、IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT表示该操作为非阻塞操作,SEM_UNDO表示当进程退出的时候会还原该进程的信号量操作可以避免死锁。
@nsops:进sops结构变量的个数,需大于或等于1。一般为1,只完成对一个信号量的操作。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg)

@semctl函数:控制信号量(&删除)
@int:成功返回非负整数,失败返回-1
@semid:信号量集标识符
@semnum:信号量集数组上的下标,表示某一个信号量
@cmd:操作类型,可取值IPC_STAT或者IPC_SET或者IPC_RMID
	IPC_STAT:从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
	IPC_SET:设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
	IPC_RMID:从内核中删除信号量集合
	GETALL:从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中
	GETNCNT:返回当前等待资源的进程个数
	GETPID:返回最后一个执行系统调用semop()进程的PID
	GETVAL:返回信号量集合内单个信号量的值
	GETZCNT:返回当前等待100%资源利用的进程个数
	SETALL:与GETALL正好相反
	SETVAL:用联合体中val成员的值设置信号量集合中单个信号量的值
@arg:输出输入信号量状态
	union semun {
	   short val;          /*SETVAL用的值*/
	   struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/
	   unsigned short* array; /*SETALL、GETALL用的数组值*/
	   struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/
	} arg;

三:实现父子进程分别向屏幕打印“AA”“BB”

/*
实现父子进程分别向屏幕打印“AA”“BB”
*/

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

//需要自定义
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;
};

void Print(int semid, char c)
{
    struct sembuf sf;
    sf.sem_num = 0;
    sf.sem_flg = SEM_UNDO;
	while (true)
	{
		sf.sem_op = -1;
		semop(semid, &sf, 1);	//P操作请求资源
		printf("%c", c);
		fflush(stdout);
		sleep(1);
		printf("%c", c);
		fflush(stdout);
		sf.sem_op = 1;
		semop(semid, &sf, 1);	//V操作释放资源
	}
}

int main(int argc, char *argv[])
{
    int semid = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT);
    if (semid == -1)
    {
    	ERR_EXIT("semget");
    }

    //初始化0信号量的val值为1
    union semun un;
    un.val = 1;
	if(semctl(semid, 0, SETVAL, un) < 0)
	{
		ERR_EXIT("semctl");
	}

    pid_t pid = fork();
    if (pid < 0)
    	ERR_EXIT("fork");
    else if (pid == 0)
	{
    	Print(semid, 'B');
	}
    else
    {
    	Print(semid, 'A');
    }

    //删除该信号量
    sleep(5);
	if(semctl(semid, 0, IPC_RMID) < 0)
	{
		ERR_EXIT("semctl");
	}
    return 0;
}
/*
结果:
root@epc:/home/share/project/test/linux/demo# ./server 
AABBAABBAABBAABBAABBA^C
*/
发布了32 篇原创文章 · 获赞 3 · 访问量 1399

猜你喜欢

转载自blog.csdn.net/m0_37582216/article/details/104640316
今日推荐