16.信号量互斥编程
16.信号量互斥编程
我们先来看一个例子。就是两个进程访问同一个文件,由于线程的先后,导致内容的异常。即是数据内容的混乱。
Student1.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main(){
//open file
int fd = 0;
fd = open("/home/wen",O_RDWR|O_APPEND);
//write something into file
write(fd,"forfish!",8);
//rest
sleep(10);
//write something else.
write(fd,"is die!",7);
close(fd);
}
Student2.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main(){
int fd = 0;
fd = open("/home/wen",O_RDWR|O_APPEND);
write(fd,"fish!",5);
close(fd);
}
运行的结果:
最后是两个人都die了。这信息错误就严重了。所以我们要有机制来控制对资源的使用,才不会产生混乱。
这就引出了信号量的概念:信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源(进程互斥)。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。
信号量的分类:
-
二值信号灯:信号灯的值只能取0或1
-
计数信号灯:信号灯的值可以取任意非负数。
接下来是使用信号量:
信号量的函数:semget:
Man 2 semget的信息:
NAME
semget - get a semaphore set identifier
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
DESCRIPTION
The semget() system call returns the semaphore set identifier associ-
ated with the argument key. A new set of nsems semaphores is created
if key has the value IPC_PRIVATE or if no existing semaphore set is
associated with key and IPC_CREAT is specified in semflg.
If semflg specifies both IPC_CREAT and IPC_EXCL and a semaphore set
already exists for key, then semget() fails with errno set to EEXIST.
(This is analogous to the effect of the combination O_CREAT | O_EXCL
for open(2).)
Upon creation, the least significant 9 bits of the argument semflg
define the permissions (for owner, group and others) for the semaphore
set. These bits have the same format, and the same meaning, as the
mode argument of open(2) (though the execute permissions are not mean-
ingful for semaphores, and write permissions mean permission to alter
semaphore values).
The values of the semaphores in a newly created set are indeterminate.
(POSIX.1-2001 is explicit on this point.) Although Linux, like many
other implementations, initializes the semaphore values to 0, a
portable application cannot rely on this: it should explicitly initial-
ize the semaphores to the desired values.
When creating a new semaphore set, semget() initializes the set's asso-
ciated data structure, semid_ds (see semctl(2)), as follows:
sem_perm.cuid and sem_perm.uid are set to the effective user ID
of the calling process.
sem_perm.cgid and sem_perm.gid are set to the effective group ID
of the calling process.
The least significant 9 bits of sem_perm.mode are set to the
least significant 9 bits of semflg.
sem_nsems is set to the value of nsems.
sem_otime is set to 0.
sem_ctime is set to the current time.
The argument nsems can be 0 (a don't care) when a semaphore set is not
being created. Otherwise nsems must be greater than 0 and less than or
equal to the maximum number of semaphores per semaphore set (SEMMSL).
If the semaphore set already exists, the permissions are verified.
RETURN VALUE
If successful, the return value will be the semaphore set identifier (a
non-negative integer), otherwise -1 is returned, with errno indicating
the error.
ERRORS
On failure errno will be set to one of the following:
EACCES A semaphore set exists for key, but the calling process does not
have permission to access the set, and does not have the
CAP_IPC_OWNER capability.
EEXIST A semaphore set exists for key and semflg specified both
IPC_CREAT and IPC_EXCL.
EINVAL nsems is less than 0 or greater than the limit on the number of
semaphores per semaphore set (SEMMSL), or a semaphore set corre-
sponding to key already exists, and nsems is larger than the
number of semaphores in that set.
ENOENT No semaphore set exists for key and semflg did not specify
IPC_CREAT.
ENOMEM A semaphore set has to be created but the system does not have
enough memory for the new data structure.
ENOSPC A semaphore set has to be created but the system limit for the
maximum number of semaphore sets (SEMMNI), or the system wide
maximum number of semaphores (SEMMNS), would be exceeded.
CONFORMING TO
SVr4, POSIX.1-2001.
NOTES
IPC_PRIVATE isn't a flag field but a key_t type. If this special value
is used for key, the system call ignores everything but the least sig-
nificant 9 bits of semflg and creates a new semaphore set (on success).
The following limits on semaphore set resources affect the semget()
call:
SEMMNI System wide maximum number of semaphore sets: policy dependent
(on Linux, this limit can be read and modified via the fourth
field of /proc/sys/kernel/sem).
SEMMSL Maximum number of semaphores per semid: implementation dependent
(on Linux, this limit can be read and modified via the first
field of /proc/sys/kernel/sem).
SEMMNS System wide maximum number of semaphores: policy dependent (on
Linux, this limit can be read and modified via the second field
of /proc/sys/kernel/sem). Values greater than SEMMSL * SEMMNI
makes it irrelevant.
BUGS
The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more
clearly show its function.
The semaphores in a set are not initialized by semget(). In order to
initialize the semaphores, semctl(2) must be used to perform a SETVAL
or a SETALL operation on the semaphore set. (Where multiple peers do
not know who will be the first to initialize the set, checking for a
non-zero sem_otime in the associated data structure retrieved by a sem-
ctl(2) IPC_STAT operation can be used to avoid races.)
SEE ALSO
semctl(2), semop(2), ftok(3), capabilities(7), sem_overview(7),
svipc(7)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A
description of the project, and information about reporting bugs, can
be found at http://www.kernel.org/doc/man-pages/.
创建/打开信号量集合的函数:semget:
函数的原型:
int semget(key_t key, int nsems, int semflg);
函数的功能是:获取信号量集合的标示符。当key所指定的信号量不存在的时候,并且semflg里面包含了IPC_CREAT,这个时候,就会创建一个信号量集合。
该函数要包含的头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
该函数的返回值:成功返回信号量集合的标示符。失败返回-1.
参数说明:
Key:键值。
Semflg:标志,可以取IPC_CREAT.
Nsems:创建的这个信号量集合里面包含的信号数目。
键值:
查看帮助文档:man 3 ftok:
NAME
ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
DESCRIPTION
The ftok() function uses the identity of the file named by the given pathname
(which must refer to an existing, accessible file) and the least significant 8
bits of proj_id (which must be non-zero) to generate a key_t type System V IPC
key, suitable for use with msgget(2), semget(2), or shmget(2).
The resulting value is the same for all pathnames that name the same file, when
the same value of proj_id is used. The value returned should be different when
the (simultaneously existing) files or the project IDs differ.
RETURN VALUE
On success the generated key_t value is returned. On failure -1 is returned,
with errno indicating the error as for the stat(2) system call.
CONFORMING TO
POSIX.1-2001.
NOTES
Under libc4 and libc5 (and under SunOS 4.x) the prototype was:
key_t ftok(char *pathname, char proj_id);
Today proj_id is an int, but still only 8 bits are used. Typical usage has an
ASCII character proj_id, that is why the behavior is said to be undefined when
proj_id is zero.
Of course no guarantee can be given that the resulting key_t is unique. Typi-
cally, a best effort attempt combines the given proj_id byte, the lower 16 bits
of the inode number, and the lower 8 bits of the device number into a 32-bit
result. Collisions may easily happen, for example between files on /dev/hda1
and files on /dev/sda1.
SEE ALSO
msgget(2), semget(2), shmget(2), stat(2), svipc(7)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A description
of the project, and information about reporting bugs, can be found at
http://www.kernel.org/doc/man-pages/.
函数的原型:
key_t ftok(const char *pathname, int proj_id);
该函数的功能是创建一个键值。第一个参数是信号量文件的路径,绝对路径。第二个参数是项目编号,编号不一样,键值就不一样。
返回值就是信号量的键值。
操作信号量
函数semop自动执行信号量集合上的操作数组,这是个原子操作。
帮助文档信息:
NAME
semop, semtimedop - semaphore operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops,
struct timespec *timeout);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
semtimedop(): _GNU_SOURCE
DESCRIPTION
Each semaphore in a semaphore set has the following associated values:
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid; /* process that did last op */
semop() performs operations on selected semaphores in the set indicated by
semid. Each of the nsops elements in the array pointed to by sops specifies an
operation to be performed on a single semaphore. The elements of this structure
are of type struct sembuf, containing the following members:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO. If an operation speci-
fies SEM_UNDO, it will be automatically undone when the process terminates.
The set of operations contained in sops is performed in array order, and atomi-
cally, that is, the operations are performed either as a complete unit, or not
at all. The behavior of the system call if not all operations can be performed
immediately depends on the presence of the IPC_NOWAIT flag in the individual
sem_flg fields, as noted below.
Each operation is performed on the sem_num-th semaphore of the semaphore set,
where the first semaphore of the set is numbered 0. There are three types of
operation, distinguished by the value of sem_op.
If sem_op is a positive integer, the operation adds this value to the semaphore
value (semval). Furthermore, if SEM_UNDO is specified for this operation, the
system updates the process undo count (semadj) for this semaphore. This opera-
tion can always proceed — it never forces a process to wait. The calling pro-
cess must have alter permission on the semaphore set.
If sem_op is zero, the process must have read permission on the semaphore set.
This is a "wait-for-zero" operation: if semval is zero, the operation can imme-
diately proceed. Otherwise, if IPC_NOWAIT is specified in sem_flg, semop()
fails with errno set to EAGAIN (and none of the operations in sops is per-
formed). Otherwise semzcnt (the count of processes waiting until this
semaphore's value becomes zero) is incremented by one and the process sleeps
until one of the following occurs:
· semval becomes 0, at which time the value of semzcnt is decremented.
· The semaphore set is removed: semop() fails, with errno set to EIDRM.
· The calling process catches a signal: the value of semzcnt is decremented and
semop() fails, with errno set to EINTR.
· The time limit specified by timeout in a semtimedop() call expires: semop()
fails, with errno set to EAGAIN.
If sem_op is less than zero, the process must have alter permission on the
semaphore set. If semval is greater than or equal to the absolute value of
sem_op, the operation can proceed immediately: the absolute value of sem_op is
subtracted from semval, and, if SEM_UNDO is specified for this operation, the
system updates the process undo count (semadj) for this semaphore. If the abso-
lute value of sem_op is greater than semval, and IPC_NOWAIT is specified in
sem_flg, semop() fails, with errno set to EAGAIN (and none of the operations in
sops is performed). Otherwise semncnt (the counter of processes waiting for
this semaphore's value to increase) is incremented by one and the process sleeps
until one of the following occurs:
· semval becomes greater than or equal to the absolute value of sem_op, at
which time the value of semncnt is decremented, the absolute value of sem_op
is subtracted from semval and, if SEM_UNDO is specified for this operation,
the system updates the process undo count (semadj) for this semaphore.
· The semaphore set is removed from the system: semop() fails, with errno set
to EIDRM.
· The calling process catches a signal: the value of semncnt is decremented and
semop() fails, with errno set to EINTR.
· The time limit specified by timeout in a semtimedop() call expires: the sys-
tem call fails, with errno set to EAGAIN.
On successful completion, the sempid value for each semaphore specified in the
array pointed to by sops is set to the process ID of the calling process. In
addition, the sem_otime is set to the current time.
semtimedop() behaves identically to semop() except that in those cases were the
calling process would sleep, the duration of that sleep is limited by the amount
of elapsed time specified by the timespec structure whose address is passed in
the timeout argument. If the specified time limit has been reached, semtime-
dop() fails with errno set to EAGAIN (and none of the operations in sops is per-
formed). If the timeout argument is NULL, then semtimedop() behaves exactly
like semop().
RETURN VALUE
If successful semop() and semtimedop() return 0; otherwise they return -1 with
errno indicating the error.
ERRORS
On failure, errno is set to one of the following:
E2BIG The argument nsops is greater than SEMOPM, the maximum number of opera-
tions allowed per system call.
EACCES The calling process does not have the permissions required to perform the
specified semaphore operations, and does not have the CAP_IPC_OWNER capa-
bility.
EAGAIN An operation could not proceed immediately and either IPC_NOWAIT was
specified in sem_flg or the time limit specified in timeout expired.
EFAULT An address specified in either the sops or the timeout argument isn't
accessible.
EFBIG For some operation the value of sem_num is less than 0 or greater than or
equal to the number of semaphores in the set.
EIDRM The semaphore set was removed.
EINTR While blocked in this system call, the process caught a signal; see sig-
nal(7).
EINVAL The semaphore set doesn't exist, or semid is less than zero, or nsops has
a non-positive value.
ENOMEM The sem_flg of some operation specified SEM_UNDO and the system does not
have enough memory to allocate the undo structure.
ERANGE For some operation sem_op+semval is greater than SEMVMX, the implementa-
tion dependent maximum value for semval.
VERSIONS
semtimedop() first appeared in Linux 2.5.52, and was subsequently backported
into kernel 2.4.22. Glibc support for semtimedop() first appeared in version
2.3.3.
CONFORMING TO
SVr4, POSIX.1-2001.
NOTES
The sem_undo structures of a process aren't inherited by the child produced by
fork(2), but they are inherited across an execve(2) system call.
semop() is never automatically restarted after being interrupted by a signal
handler, regardless of the setting of the SA_RESTART flag when establishing a
signal handler.
semadj is a per-process integer which is simply the (negative) count of all
semaphore operations performed specifying the SEM_UNDO flag. When a semaphore's
value is directly set using the SETVAL or SETALL request to semctl(2), the cor-
responding semadj values in all processes are cleared.
The semval, sempid, semzcnt, and semnct values for a semaphore can all be
retrieved using appropriate semctl(2) calls.
The following limits on semaphore set resources affect the semop() call:
SEMOPM Maximum number of operations allowed for one semop() call (32) (on Linux,
this limit can be read and modified via the third field of /proc/sys/ker-
nel/sem).
SEMVMX Maximum allowable value for semval: implementation dependent (32767).
The implementation has no intrinsic limits for the adjust on exit maximum value
(SEMAEM), the system wide maximum number of undo structures (SEMMNU) and the
per-process maximum number of undo entries system parameters.
BUGS
When a process terminates, its set of associated semadj structures is used to
undo the effect of all of the semaphore operations it performed with the
SEM_UNDO flag. This raises a difficulty: if one (or more) of these semaphore
adjustments would result in an attempt to decrease a semaphore's value below
zero, what should an implementation do? One possible approach would be to block
until all the semaphore adjustments could be performed. This is however unde-
sirable since it could force process termination to block for arbitrarily long
periods. Another possibility is that such semaphore adjustments could be
ignored altogether (somewhat analogously to failing when IPC_NOWAIT is specified
for a semaphore operation). Linux adopts a third approach: decreasing the
semaphore value as far as possible (i.e., to zero) and allowing process termina-
tion to proceed immediately.
In kernels 2.6.x, x <= 10, there is a bug that in some circumstances prevents a
process that is waiting for a semaphore value to become zero from being woken up
when the value does actually become zero. This bug is fixed in kernel 2.6.11.
EXAMPLE
The following code segment uses semop() to atomically wait for the value of
semaphore 0 to become zero, and then increment the semaphore value by one.
struct sembuf sops[2];
int semid;
/* Code to set semid omitted */
sops[0].sem_num = 0; /* Operate on semaphore 0 */
sops[0].sem_op = 0; /* Wait for value to equal 0 */
sops[0].sem_flg = 0;
sops[1].sem_num = 0; /* Operate on semaphore 0 */
sops[1].sem_op = 1; /* Increment value by one */
sops[1].sem_flg = 0;
if (semop(semid, sops, 2) == -1) {
perror("semop");
exit(EXIT_FAILURE);
}
SEE ALSO
semctl(2), semget(2), sigaction(2), capabilities(7), sem_overview(7), svipc(7),
time(7)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A description
of the project, and information about reporting bugs, can be found at
http://www.kernel.org/doc/man-pages/.
该函数里:函数的原型:
int semop(int semid, struct sembuf *sops, unsigned nsops);
该函数的功能是操作信号量,通常是以集合出现的。
需要的头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
该函数的返回值:成功返回0,失败返回-1.
参数说明:
semid:要操作的信号量集合的标示符。
nsops:要操作多少个信号量。
Sops:对信号量执行的操作:是获取还是释放。获取-1,释放+1.Sem_op:正数是释放,负数是获取。如果获取不成功则会进入等待状态。
实例:
semaphoreA.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void main(){
//open file
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
key = ftok("/home",1);
//create semaphore
semid = semget(key,1,IPC_CREAT);
fd = open("/home/wen",O_RDWR|O_APPEND);
//get semaphore
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops, 1);
//write something into file
write(fd,"forfish!",8);
//rest
sleep(10);
//write something else.
write(fd,"is die!",7);
//free semaphore
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops, 1);
close(fd);
}
semaphoreB.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
key = ftok("/home",1);
semid = semget(key,1,IPC_CREAT);
//open sem
fd = open("/home/wen",O_RDWR|O_APPEND);
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops, 1);
write(fd,"fish!",5);
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops, 1);
close(fd);
}
编译运行:
结果看不见B等待,结果:
还是没能实现!这是因为我们忽略了信号量的初始值。我们在操作之前应该确保信号量的值是1.所以接下来就是设置我们的信号量为1.
设置信号量:
在命令行:man 2 semctl:
NAME
semctl - semaphore control operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
DESCRIPTION
semctl() performs the control operation specified by cmd on the semaphore set
identified by semid, or on the semnum-th semaphore of that set. (The semaphores
in a set are numbered starting at 0.)
This function has three or four arguments, depending on cmd. When there are
four, the fourth has the type union semun. The calling program must define this
union as follows:
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) */
};
The semid_ds data structure is defined in <sys/sem.h> as follows:
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned short sem_nsems; /* No. of semaphores in set */
};
The ipc_perm structure is defined in <sys/ipc.h> as follows (the highlighted
fields are settable using IPC_SET):
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
Valid values for cmd are:
IPC_STAT Copy information from the kernel data structure associated with semid
into the semid_ds structure pointed to by arg.buf. The argument sem-
num is ignored. The calling process must have read permission on the
semaphore set.
IPC_SET Write the values of some members of the semid_ds structure pointed to
by arg.buf to the kernel data structure associated with this semaphore
set, updating also its sem_ctime member. The following members of the
structure are updated: sem_perm.uid, sem_perm.gid, and (the least sig-
nificant 9 bits of) sem_perm.mode. The effective UID of the calling
process must match the owner (sem_perm.uid) or creator (sem_perm.cuid)
of the semaphore set, or the caller must be privileged. The argument
semnum is ignored.
IPC_RMID Immediately remove the semaphore set, awakening all processes blocked
in semop(2) calls on the set (with an error return and errno set to
EIDRM). The effective user ID of the calling process must match the
creator or owner of the semaphore set, or the caller must be privi-
leged. The argument semnum is ignored.
IPC_INFO (Linux-specific)
Returns information about system-wide semaphore limits and parameters
in the structure pointed to by arg.__buf. This structure is of type
seminfo, defined in <sys/sem.h> if the _GNU_SOURCE feature test macro
is defined:
struct seminfo {
int semmap; /* Number of entries in semaphore
map; unused within kernel */
int semmni; /* Maximum number of semaphore sets */
int semmns; /* Maximum number of semaphores in all
semaphore sets */
int semmnu; /* System-wide maximum number of undo
structures; unused within kernel */
int semmsl; /* Maximum number of semaphores in a
set */
int semopm; /* Maximum number of operations for
semop(2) */
int semume; /* Maximum number of undo entries per
process; unused within kernel */
int semusz; /* Size of struct sem_undo */
int semvmx; /* Maximum semaphore value */
int semaem; /* Max. value that can be recorded for
semaphore adjustment (SEM_UNDO) */
};
The semmsl, semmns, semopm, and semmni settings can be changed via
/proc/sys/kernel/sem; see proc(5) for details.
SEM_INFO (Linux-specific)
Returns a seminfo structure containing the same information as for
IPC_INFO, except that the following fields are returned with informa-
tion about system resources consumed by semaphores: the semusz field
returns the number of semaphore sets that currently exist on the sys-
tem; and the semaem field returns the total number of semaphores in
all semaphore sets on the system.
SEM_STAT (Linux-specific)
Returns a semid_ds structure as for IPC_STAT. However, the semid
argument is not a semaphore identifier, but instead an index into the
kernel's internal array that maintains information about all semaphore
sets on the system.
GETALL Return semval (i.e., the current value) for all semaphores of the set
into arg.array. The argument semnum is ignored. The calling process
must have read permission on the semaphore set.
GETNCNT The system call returns the value of semncnt (i.e., the number of pro-
cesses waiting for the value of this semaphore to increase) for the
semnum-th semaphore of the set (i.e., the number of processes waiting
for an increase of semval for the semnum-th semaphore of the set).
The calling process must have read permission on the semaphore set.
GETPID The system call returns the value of sempid for the semnum-th
semaphore of the set (i.e., the PID of the process that executed the
last semop(2) call for the semnum-th semaphore of the set). The call-
ing process must have read permission on the semaphore set.
GETVAL The system call returns the value of semval for the semnum-th
semaphore of the set. The calling process must have read permission
on the semaphore set.
GETZCNT The system call returns the value of semzcnt (i.e., the number of pro-
cesses waiting for the value of this semaphore to become zero) for the
semnum-th semaphore of the set (i.e., the number of processes waiting
for semval of the semnum-th semaphore of the set to become 0). The
calling process must have read permission on the semaphore set.
SETALL Set semval for all semaphores of the set using arg.array, updating
also the sem_ctime member of the semid_ds structure associated with
the set. Undo entries (see semop(2)) are cleared for altered
semaphores in all processes. If the changes to semaphore values would
permit blocked semop(2) calls in other processes to proceed, then
those processes are woken up. The argument semnum is ignored. The
calling process must have alter (write) permission on the semaphore
set.
SETVAL Set the value of semval to arg.val for the semnum-th semaphore of the
set, updating also the sem_ctime member of the semid_ds structure
associated with the set. Undo entries are cleared for altered
semaphores in all processes. If the changes to semaphore values would
permit blocked semop(2) calls in other processes to proceed, then
those processes are woken up. The calling process must have alter
permission on the semaphore set.
RETURN VALUE
On failure semctl() returns -1 with errno indicating the error.
Otherwise the system call returns a non-negative value depending on cmd as fol-
lows:
GETNCNT the value of semncnt.
GETPID the value of sempid.
GETVAL the value of semval.
GETZCNT the value of semzcnt.
IPC_INFO the index of the highest used entry in the kernel's internal array
recording information about all semaphore sets. (This information
can be used with repeated SEM_STAT operations to obtain information
about all semaphore sets on the system.)
SEM_INFO As for IPC_INFO.
SEM_STAT the identifier of the semaphore set whose index was given in semid.
All other cmd values return 0 on success.
ERRORS
On failure, errno will be set to one of the following:
EACCES The argument cmd has one of the values GETALL, GETPID, GETVAL, GETNCNT,
GETZCNT, IPC_STAT, SEM_STAT, SETALL, or SETVAL and the calling process
does not have the required permissions on the semaphore set and does not
have the CAP_IPC_OWNER capability.
EFAULT The address pointed to by arg.buf or arg.array isn't accessible.
EIDRM The semaphore set was removed.
EINVAL Invalid value for cmd or semid. Or: for a SEM_STAT operation, the index
value specified in semid referred to an array slot that is currently
unused.
EPERM The argument cmd has the value IPC_SET or IPC_RMID but the effective user
ID of the calling process is not the creator (as found in sem_perm.cuid)
or the owner (as found in sem_perm.uid) of the semaphore set, and the
process does not have the CAP_SYS_ADMIN capability.
ERANGE The argument cmd has the value SETALL or SETVAL and the value to which
semval is to be set (for some semaphore of the set) is less than 0 or
greater than the implementation limit SEMVMX.
CONFORMING TO
SVr4, POSIX.1-2001.
NOTES
The IPC_INFO, SEM_STAT and SEM_INFO operations are used by the ipcs(8) program
to provide information on allocated resources. In the future these may modified
or moved to a /proc file system interface.
Various fields in a struct semid_ds were typed as short under Linux 2.2 and have
become long under Linux 2.4. To take advantage of this, a recompilation under
glibc-2.1.91 or later should suffice. (The kernel distinguishes old and new
calls by an IPC_64 flag in cmd.)
In some earlier versions of glibc, the semun union was defined in <sys/sem.h>,
but POSIX.1-2001 requires that the caller define this union. On versions of
glibc where this union is not defined, the macro _SEM_SEMUN_UNDEFINED is defined
in <sys/sem.h>.
The following system limit on semaphore sets affects a semctl() call:
SEMVMX Maximum value for semval: implementation dependent (32767).
For greater portability it is best to always call semctl() with four arguments.
SEE ALSO
ipc(2), semget(2), semop(2), capabilities(7), sem_overview(7), svipc(7)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A description
of the project, and information about reporting bugs, can be found at
http://www.kernel.org/doc/man-pages/.
该函数的原型:
int semctl(int semid, int semnum, int cmd, ...);
第一个参数是要操作信号量的集合,第二个是集合里的第几个信号,第三个参数是命令,即是进行的操作:
GETNCNT the value of semncnt.
GETPID the value of sempid.
GETVAL the value of semval.
GETZCNT the value of semzcnt.
IPC_INFO the index of the highest used entry in the kernel's internal array
recording information about all semaphore sets. (This information
can be used with repeated SEM_STAT operations to obtain information
about all semaphore sets on the system.)
SEM_INFO As for IPC_INFO.
SEM_STAT the identifier of the semaphore set whose index was given in semid.
返回值就是信号量的值。
semaphoreA.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void main(){
//open file
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
int temp;
key = ftok("/home",1);
//create semaphore
semid = semget(key,1,IPC_CREAT);
temp = semctl(semid,0,GETVAL);
printf("sem of init is %d\n",temp);
fd = open("/home/wen",O_RDWR|O_APPEND);
//get semaphore
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops, 1);
//write something into file
write(fd,"forfish!",8);
//rest
sleep(10);
//write something else.
write(fd,"is die!",7);
//free semaphore
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops, 1);
close(fd);
}
运行的效果:
我们看到信号量的初值为2,所以我们上面才会失败。所以我们需要把信号量的初值设置为1.也是在semctl函数修改:
temp = semctl(semid,0,GETVAL,1);
最后的代码:
semaphoreA.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void main(){
//open file
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
int temp;
key = ftok("/home",1);
//create semaphore
semid = semget(key,1,IPC_CREAT);
temp = semctl(semid,0,SETVAL,1);
printf("sem of init is %d\n",temp);
fd = open("/home/wen",O_RDWR|O_APPEND);
//get semaphore
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops, 1);
//write something into file
write(fd,"forfish!",8);
//rest
sleep(10);
//write something else.
write(fd,"is die!",7);
//free semaphore
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops, 1);
close(fd);
}
semaphoreB.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
int temp;
key = ftok("/home",1);
semid = semget(key,1,IPC_CREAT);
//open sem
temp = semctl(semid,0,GETVAL,1);
printf("sem of value is %d\n",temp);
fd = open("/home/wen",O_RDWR|O_APPEND);
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops, 1);
write(fd,"fish!",5);
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops, 1);
close(fd);
}
运行结果:
写入的结果:
可以看到现在两个进程往同一个文件写入内容就不会混乱了。这是因为我们设置了初始的信号量是1,当semaphoreA往里面写东西,然后休息10秒,由于在这段时间,信号量被A占有着,没有释放。所以即使B想往该文件写入内容。因为此时被A占有着,信号量为0,此时B只能等待。我们看到执行中,B确实在等待。直到A写完成,释放了信号量。B才能占有信号量,往文件写内容。显示的结果和我们想要的一样。操作成功。