上篇文章我们分析了,linux下多线程的同步和互斥,使用起来是比较简单和方便的。但是对与进程来说,却有着进程的特殊性:
(1)在linux下,子进程是父进程的拷贝,注意:拷贝,即二者的内容是完全隔离的,相互没有影响,像全局变量,互斥锁等资源也是拷贝的,相互独立的。
(2)要用Semaphore 来实现进程的同步和互斥
下面我们就来看看,如何利用信号灯实现linux下多进程的同步和互斥。
1 信号灯函数的定义
#include
int semctl(int sem_id, int sem_num, int command, ...);
int semget(key_t key, int num_sems, int sem_flags);
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
(1)int semget(key_t key, int num_sems, int sem_flags);
这个函数创建了一个新的信号量,或者是得到一个已经存在的信号量的key
The first parameter, key, is an integral value used to allow unrelated processes to access the same sema-phore. All semaphores are accessed indirectly by the program supplying a key, for which the system then generates a semaphore identifier. The semaphore key is used only with semget. All other semaphore functions use the semaphore identifier returned from semget.
There is a special semaphore key value, IPC_PRIVATE, that is intended to create a semaphore that only the creating process could access, but this rarely has any useful purpose. You should provide a unique,non-zero integer value for key when you want to create a new semaphore.
The num_sems parameter is the number of semaphores required. This is almost always 1.
The sem_flags parameter is a set of flags, very much like the flags to the open function. The lower ninebits are the permissions for the semaphore, which behave like file permissions. In addition, these can be bit-wise ORed with the value IPC_CREAT to create a new semaphore. It’s not an error to have the IPC_CREAT flag set and give the key of an existing semaphore. The IPC_CREAT flag is silently ignored if it is not required. You can use IPC_CREAT and IPC_EXCL together to ensure that you obtain a new, unique sema-phore. It will return an error if the semaphore already exists.
The semget function returns a positive (nonzero) value on success; this is the semaphore identifier used in the other semaphore functions. On error, it returns –1.
(2)nt semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
这个函数用来改变semaphore的值。
The first parameter, sem_id, is the semaphore identifier, as returned from semget. The second parameter,sem_ops, is a pointer to an array of structures, each of which will have at least the following members:
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
}
The first member, sem_num, is the semaphore number, usually 0 unless you’re working with an array of semaphores. The sem_op member is the value by which the semaphore should be changed. (You can change a semaphore by amounts other than 1.) In general, only two values are used, –1, which is your P operation to wait for a semaphore to become available, and +1, which is your V operation to signal that a semaphore is now available.The final member,sem_flg, is usually set to SEM_UNDO. This causes the operating system to track the changes made to the semaphore by the current process and, if the process terminates without releasing the semaphore, allows the operating system to automatically release the semaphore if it was held by this process. It’s good practice to set sem_flg to SEM_UNDO, unless you specifically require different behavior. If you do decide you need a value other than SEM_UNDO, it’s important to be consistent, or you can get very confused as to whether the kernel is attempting to “tidy up” your semaphores when your process exits.All actions called for by semop are taken together to avoid a race condition implied by the use of multiple semaphores. You can find full details of the processing of semop in the manual pages.
(3)int semctl(int sem_id, int sem_num, int command, ...);
直接控制信号量信息
The first parameter, sem_id, is a semaphore identifier, obtained from semget. The sem_num parameter is the semaphore number. You use this when you’re working with arrays of semaphores. Usually, this is0, the first and only semaphore. The command parameter is the action to take, and a fourth parameter, if present, is a union semun, which according to the X/OPEN specification must have at least the follow-ing members:
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
Most versions of Linux have a definition of the semun union in a header file (usually sem.h), though X/Open does say that you have to declare your own. If you do find that you need to declare your own,check the manual pages for semctl to see if there is a definition given. If there is, we suggest you use exactly the definition given in your manual, even if it differs from that given here.There are many different possible values of command allowed for semctl. Only the two that we describe here are commonly used. For full details of the semctl function, you should consult the manual page.
The two common values of command are:
❑ SETVAL: Used for initializing a semaphore to a known value. The value required is passed as the val member of the union semun. This is required to set the semaphore up before it’s used for the first time.
❑ IPC_RMID: Used for deleting a semaphore identifier when it’s no longer required.
The semctl function returns different values depending on the command parameter. For SETVAL andIPC_RMID it returns 0 for success and –1 on error.
2 如何使用semaphore
#include
#include
#include
#include
#include “semun.h”
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = ‘O’;
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if (argc > 1) {
if (!set_semvalue()) {
fprintf(stderr, “Failed to initialize semaphore\n”);
exit(EXIT_FAILURE);
}
op_char = ‘X’;
sleep(2);
}
2. Then you have a loop that enters and leaves the critical section 10 times. There you first make a call to semaphore_p, which sets the semaphore to wait as this program is about to enter the critical section:
for(i = 0; i < 10; i++) {
if (!semaphore_p()) exit(EXIT_FAILURE);
printf(“%c”, op_char);fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf(“%c”, op_char);fflush(stdout);
3. After the critical section, you call semaphore_v, setting the semaphore as available, before going through the for loop again after a random wait. After the loop, the call to del_semvalue is made to clean up the code:
if (!semaphore_v()) exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);
}
printf(“\n%d - finished\n”, getpid());
if (argc > 1) {
sleep(10);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
4. The function set_semvalue initializes the semaphore using the SETVAL command in a semctl call. You need to do this before you can use the semaphore
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
return(1);
}
5. The del_semvalue function has almost the same form, except that the call to semctl uses the command IPC_RMID to remove the semaphore’s ID:
static void del_semvalue(void)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, “Failed to delete semaphore\n”);
}
6. semaphore_p changes the semaphore by –1. This is the “wait” operation:
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, “semaphore_p failed\n”);
return(0);
}
return(1);
}
7. semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1. This is the “release” operation, so that the semaphore becomes available:
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, “semaphore_v failed\n”);
return(0);
}
return(1);
}
阅读(13987) | 评论(0) | 转发(0) |