进程间通信——信号量

有关结构体
1.sem
struct sem {
short sempid; /* pid of last operation */
ushort semval; /* current value */
ushort semncnt; /* num procs awaiting increase in semval */
ushort semzcnt; /* num procs awaiting semval = 0 */
};
其中,
sem_pid 成员保存了最近一次操作信号量的进程的pid。
sem_semval 成员保存着信号量的计数值。
sem_semncnt 成员保存着等待使用资源的进程数目。
sem_semzcnt 成员保存等待资源完全空闲的的进程数目。

2.semun
semun联合体在senctl()函数中使用,提供 senctl()操作所需要的信息。
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
ushort *array; /* array for GETALL & SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
void *__pad;
};

3.sembuf
sembuf结构体被semop()函数用来定义对信号量对象的基本操作
struct sembuf {
unsigned short sem_num; /* semaphore index in array */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
其中,
sem_num 成员为接受操作的信号量在信号量数组中的序号(数组下标)。
sem_op 成员定义了进行的操作(可以是正、负和零)。
sem_flg 是控制操作行为的标志。

4.semid_qs
和msgqid_ds类似,semid_qs结构被系统用来储存每个信号量对象的有关信息。
struct semid_ds {
struct ipc_perm sem_perm; /* permissions .. see ipc.h */
__kernel_time_t sem_otime; /* last semop time */
__kernel_time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
struct sem_queue *sem_pending; /* pending operations to be processed */
struct sem_queue **sem_pending_last; /* last pending operation */
struct sem_undo *undo; /* undo requests on this array */
unsigned short sem_nsems; /* no. of semaphores in array */
};

其中,
sem_perm 成员保存了信号量对象的存取权限以及其他一些信息。
sem_otime 成员保存了最近一次semop()操作的时间。
sem_ctime 成员保存了信号量对象最近一次改动发生的时间。
sem_base 指针保存着信号量数组的起始地址。
sem_pending 指针保存着还没有进行的操作。
sem_pending_last 指针保存着最后一个还没有进行的操作。
sem_undo 成员保存了 undo请求的数目。
sem_nsems 成员保存了信号量数组的成员数目。

有关的函数
1.semget()
使用semget()函数来建立新的信号量对象或者获取已有对象的标识符。
系统调用: semget()
函数声明: int semget(key_t key,int nsems,int semflg);
返回值: semaphore set IPC identifier on success
int open_semaphore_set(key_t keyval, int numsems)
{
int sid;
if(!numsems) return(-1);
if((sid=semget(keyval,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
函数的第二个参数 nsems 是信号量对象所特有的,它指定了新生成的信号量对象中信号量的数目,也就是信号量数组成员的个数。
2.semop()
使用这个函数来改变信号量对象中各个信号量的状态。
系统调用: semop()
函数声明: int semop(int semid, struct sembuf *sops, unsigned int nsops);
返回值: 0 on success (all operations performed)
第一个参数 semid是要操作的信号量对象的标识符。
第二个参数 sops是sembuf的数组,它定义了semop()函数所要进行的操作序列。
第三个参数 nsops保存着sops数组的长度,也即semop()函数将进行的操作个数。

3.semctl()
和消息队列的msgctl()函数类似,semctl()函数被用来直接对信号量对象进行控制
系统调用: semctl()
函数声明: int semctl(int semid, int semnum, int cmd, union semun arg);
返回值: positive integer on success
比较一下这两个函数的参数我们回发现一些细微的差别。首先,因为信号量对象事实上是多个信息量的集合而非单一的个体,所以在进行操作时,不仅需要指定对象的标识符,还需要用信号量在集合中的序号来指定具体的信号量个体。

两个函数都有cmd参数, 指定了函数进行的具体操作。 不过,和msgctl()函数相比, semctl()函数可以进行的操作要多得多:
IPC_STAT 取得信号量对象的 semid_ds 结构信息,并将其储存在 arg 参数中 buf 指针所指内存中返回。
IPC_SET 用 arg 参数中 buf 的数据来设定信号量对象的的 semid_ds 结构信息。和消息队列对象一样,能被这个函数设定的只有少数几个参数。
IPC_RMID 从内存中删除信号量对象。
GETALL 取得信号量对象中所有信号量的值,并储存在 arg 参数中的 array 数组中返回。
GETNCNT 返回正在等待使用某个信号量所控制的资源的进程数目。
GETPID 返回最近一个对某个信号量调用semop()函数的进程的 pid。
GETVAL 返回对象那某个信号量的数值。
GETZCNT 返回正在等待某个信号量所控制资源被全部使用的进程数目。
SETALL 用 arg 参数中 array数组的值来设定对象内各个信号量的值。
SETVAL 用 arg 参数中val成员的值来设定对象内某个信号量的值。

int  get_sem_val(int sid, int semnum)
{
return(semctl(sid,semnum,GETVAL,0));
}
上面的代码返回信号量对象中某个信号量的值。注意这里 semctl()函数的最后一个参数取的是零,这是因为执行 GETVAL 命令时这个参数被自动忽略了。
void init_semaphore(int sid, int semnum, int initval)
{
union semun semopts;
semopts.val=initval;
semctl(sid, semnum, SETVAL, semopts);

}
上面的代码用 initval参数来设定信号量对象中某个信号量的值。
在消息队列和信号量对象中,都有 IPC_STAT 和 IPC_SET 的操作。但是由于传递参数的类型不同,造成了它们在使用上的差别。在 msgctl()函数中,IPC_STAT 操作只是简单的将内核内 msgqid_ds 结

构的地址赋予buf参数(是一个指针)。而在semctl()函数中, IPC_STAT操作是将 semid_ds 的内容拷贝到 arg 参数的 buf 成员指针所指的内存中。所以,下面的代码会产生错误,而 msgctl()函数的

类似代码却不会:
void getmode(int sid)
{
int rc;
union semun semopts;
/*下面的语句会产生错误*/
if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
{
perror("semctl");
}
printf("Pemission Mode were %o\n", semopts.buf->sem_perm.mode);
return;
}
为什么呢?因为实现没有给 buf 指针分配内存,其指向是不确定的。这种“不定向”的指针是 C 程序中最危险的陷阱之一。改正这个错误,只需要提前给 buf 指针准备一块内存。下面是修改过的代码:
void getmode(int sid)
{
int rc;
union semun semopts;
struct semid_ds mysemds;
/*给buf指针准备一块内存*/
semopts.buf=&mysemds;

/*现在OK了*/
if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
{
perror("semctl");
}
printf("Pemission Mode were %o\n", semopts.buf->sem_perm.mode);
return;

}

实例:

semtool.c

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

#define SEM_RESOURCE_MAX 1 /*Initial value of all semaphores*/

void opensem(int *sid, key_t key);
void createsem(int *sid, key_t key, int members);
void locksem(int sid, int member);
void unlocksem(int sid, int member);
void removesem(int sid);
unsigned short get_member_count(int sid);
int getval(int sid, int member);
void dispval(int sid,int member);
void changemode(int sid, char *mode);
void usage(void);

int main(int argc, char *argv[])
{
key_t key;
int semset_id;
if(argc == 1) usage();

/*Create unique key via call to ftok()*/
key=ftok(".",'s');
switch(tolower(argv[1][0]))
{

case 'c':
if(argc!=3)usage();
createsem(&semset_id, key, atoi(argv[2]));
break;

case 'l':
if(argc!=3) usage();
opensem(&semset_id, key);
locksem(semset_id, atoi(argv[2]));
break;

case 'u':
if(argc!=3) usage();
opensem(&semset_id, key);
unlocksem(semset_id, atoi(argv[2]));
break;

case 'd':
opensem(&semset_id,key);
removesem(semset_id);
break;

case 'm':
opensem(&semset_id, key);
changemode(semset_id, argv[2]);
break;

default:
usage();


}
return(0);

}

void opensem(int *sid, key_t key)
{
/*Open the semaphore set ---do not creat!*/
if((*sid=semget(key, 0 , 0666)==-1)
{

printf("Semaphore set does not exist!\n");
exit(1);
}

}

void createsem(int *sid, key_t key, int members)
{
int cntr;
union semun semopts;
if(members > SEMMSL){
printf("Sorry,max number of semaphores in a set is %d\n",SEMMSL);
exit(1);

}
printf("Attempting to create new semaphore set with %d members\n",members);
if((*sid=semget(key, members, IPC_CREAT|IPC_EXCL|0666))==-1)
{
fprintf(stderr,"Semaphore set already exist!\n");
exit(1);

}

semopts.val= SEM_RESOURCE_MAX;
/*Initialize all members(could be done with SETALL)*/
for(cntr=0;cntr<members;cntr++)
{
semctl(*sid, cntr, SETVAL, semopts);
}

}


void locksem(int sid, int member)
{
struct sembuf sem_lock={0, -1, IPC_NOWAIT};
if(member <0 ||member>(get_member_count(sid) -1))
{
fprintf(stderr,"semaphore member %d out of range\n", member);
return;


}

/*Attempt to lock the semphore set*/
if(!getval(sid, member))
{
fprintf(stderr,"Semaphore resources exhausted (no lock)\n")
exit(1);
}

sem_lock.sem_num =member;

if((semop(sid, &sem_lock, 1)==-1)
{
fprintf, "Lock faild\n");
exit(1);


}
else

printf("Semaphore resources decremented by one (locked)\n");
dispval(sid ,member);


}


void unlocksem(int sid, int member)
{

struct sembuf sem_unlock={member, 1, IPC_NOWAIT};
int semval;
if(member<0 || member>(get_member_count(sid)-1))
{
fprintf(stderr,"Semaphore member %d out of range\n",member);
return;

}

/*Is the semaphore set locked? */

semval =getval(sid, member);
if(semval==SEM_REOURSE_MAX){

fprintf(stderr, "Semaphore not locked!\n");
exit(1);

}

sem_unlock.sem_num = member;

/*Attempt to lock the semaphore set*/
if((semop(sid, &sem_unlock, 1))==-1)
{
fprintf(stderr, "Unlock failed\n");
exit

}
else
printf("Semaphore resources incremented by one(unlocked)\n");
dispval(sid, member);


}


void removesem(int sid)
{
semctl(sid, 0, IPC_RMID,0);
print("Semaphore removed\n");

}

unsigned short get_member_count(int sid)
{

union semum semopts;
struct semid_ds mysemds;

semopts.buf= &mysemds;
/*Return number of member in the semaphore set*/
int rc;
if((rc=semctl(sid, 0, IPC_STAT, semopts))==-1)
{
perror("semctl");
return(-1)
}

return(semopts.buf->sem_nsems);

}

int getval(int sid, int member)
{
int semval;
semval= semctl(sid, member,GETVAL, 0)
reval semval;

}

void changemode(int sid, char *mode)
{
int rc;
union semun semopts;
struct semid_ds mysemds;

/*Get current values for internal data structure */

semopts.buf=&mysemds;
rc = semctl(sid, 0, IPC_STAT, semopts);
if(rc ==-1)
{
perror("semctl");
exit(1);

}

printf("Old permission were %o\n", semopts.buf->perm.mode);

/* Change the permission on the semaphore */
sscanf(mode,"%ho",&semopts.buf->sem_perm.mode);

/*Update the internal data structure */
semctl(sid,0, IPC_SET, semopts);

printf("Updated......\n");


}

void dispval(int sid, int member)
{
int semval;
semval= semctl(sid, member, GETVAL,0);
printf("semval for member %d is %d\n", member ,semval);


}


void usage(void)
{
fprintf(stderr, "semtool -Autility for thinking with semaphores\n");
fprintf(stderr, "\nUSAGE: semtool (c)reate <semcount>\n");
fprintf(stderr, " (l)ock <sem #>\n");
fprintf(stderr, " (u)nlock <sem #>\n");
fprintf(stderr, " (d)elete\n");
fprintf(stderr, " (m)ode <mode>\n");
exit(1);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信号量是一种用于进程间通信和同步的机制。它是一个计数器,用于保证在共享资源上的互斥访问。在Linux系统中,可以使用信号量来实现进程间的同步和互斥。以下是信号量的基本概念: - 计数器:信号量的值是一个计数器,它可以被多个进程共享。 - P操作:当一个进程需要访问共享资源时,它必须执行P操作,该操作会将信号量的值减1。如果信号量的值为0,则进程将被阻塞,直到信号量的值大于0。 - V操作:当一个进程使用完共享资源后,它必须执行V操作,该操作会将信号量的值加1。如果有进程正在等待该信号量,则唤醒其中一个进程继续执行。 在ZUCC中,可以使用信号量来实现进程的同步和互斥。首先,需要使用semget函数创建一个信号量集合,并使用semctl函数对信号量进行初始化。然后,可以使用semop函数执行P和V操作。例如,下面是一个简单的示例程序,用于演示信号量的使用: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sem.h> #define SEM_KEY 1234 union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { int semid, pid; union semun arg; struct sembuf sb; // 创建信号量集合 semid = semget(SEM_KEY, 1, IPC_CREAT | 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化信号量 arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } // 创建子进程 pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Child process\n"); // 子进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } else { // 父进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Parent process\n"); // 父进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } return 0; } ``` 在上述代码中,创建了一个信号量集合,并将其初始化为1。然后,创建了一个子进程和一个父进程,它们分别执行P和V操作。由于信号量的初始值为1,因此父进程和子进程都可以顺利地执行。如果将信号量的初始值改为0,那么父进程和子进程都将被阻塞,直到有一个进程执行V操作为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值