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了。这信息错误就严重了。所以我们要有机制来控制对资源的使用,才不会产生混乱。

这就引出了信号量的概念:信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源(进程互斥)。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。

信号量的分类:

  1. 二值信号灯:信号灯的值只能取0或1
  2. 计数信号灯:信号灯的值可以取任意非负数。

接下来是使用信号量:

信号量的函数: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 ",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 ",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 ",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才能占有信号量,往文件写内容。显示的结果和我们想要的一样。操作成功。

原文地址:https://www.cnblogs.com/FORFISH/p/5188658.html