星期六, 5月 12, 2007

IPC API

V1.0

copyright@2006

email: mesmerli@hotmail.com

前言

本文介紹 Linux 下,IPC 相關的 API。

  1. Semaphone: 本文介紹的是使用於Process 之間的同步機制,注意 pthread 與 process 使用不同的 API。
  2. Shared Memory: 利用共享記憶體機制於 Process 之間傳遞訊息。
  3. Message: 利用信箱機制於 Process 之間傳遞訊息。


使用 semget 建立 semaphone,每個 semaphore 使用 key 值識別。

相同的 key 值的 semaphone,在不同的 process 之間視為相同的 semaphore

sem1.c

/* After the #includes, the function prototypes and the global variable, we come to the
main function. There the semaphore is created with a call to semget, which returns the
semaphore ID. If the program is the first to be called (i.e. it's called with a parameter
and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
set to X. */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#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 semaphoren");
exit(EXIT_FAILURE);
}
op_char = 'X';
sleep(2);
}

/* Then we have a loop which enters and leaves the critical section ten times.
There, we 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);

/* After the critical section, we call semaphore_v, setting the semaphore 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 - finishedn", getpid());

if (argc > 1) {
sleep(10);
del_semvalue();
}

exit(EXIT_SUCCESS);
}

/* The function set_semvalue initializes the semaphore using the SETVAL command in a
semctl call. We need to do this before we 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);
}

/* The del_semvalue function has almost the same form, except 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 semaphoren");
}

/* semaphore_p changes the semaphore by -1 (waiting). */

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 failedn");
return(0);
}
return(1);
}

/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
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 failedn");
return(0);
}
return(1);
}


使用 shmget 建立 shared memory 每個 shared memory 使用 key 值識別。

相同的 key 值的 shared memory 在不同的 process 之間視為相同的 shared memory

shm1.c

/* Our first program is a consumer. After the headers the shared memory segment
(the size of our shared memory structure) is created with a call to shmget,
with the IPC_CREAT bit specified. */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;

srand((unsigned int)getpid());

shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

if (shmid == -1) {
fprintf(stderr, "shmget failedn");
exit(EXIT_FAILURE);
}

/* We now make the shared memory accessible to the program. */

shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failedn");
exit(EXIT_FAILURE);
}

printf("Memory attached at %Xn", (int)shared_memory);

/* The next portion of the program assigns the shared_memory segment to shared_stuff,
which then prints out any text in written_by_you. The loop continues until end is found
in written_by_you. The call to sleep forces the consumer to sit in its critical section,
which makes the producer wait. */

shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running) {
if (shared_stuff->written_by_you) {
printf("You wrote: %s", shared_stuff->some_text);
sleep( rand() % 4 ); /* make the other process wait for us ! */
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
running = 0;
}
}
}

/* Lastly, the shared memory is detached and then deleted. */

if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failedn");
exit(EXIT_FAILURE);
}

if (shmctl(shmid, IPC_RMID, 0) == -1) {
fprintf(stderr, "shmctl(IPC_RMID) failedn");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

shm2.c

/* The second program is the producer and allows us to enter data for consumers.
It's very similar to shm1.c and looks like this. */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;

shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

if (shmid == -1) {
fprintf(stderr, "shmget failedn");
exit(EXIT_FAILURE);
}

shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failedn");
exit(EXIT_FAILURE);
}

printf("Memory attached at %Xn", (int)shared_memory);

shared_stuff = (struct shared_use_st *)shared_memory;
while(running) {
while(shared_stuff->written_by_you == 1) {
sleep(1);
printf("waiting for client...n");
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);

strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;

if (strncmp(buffer, "end", 3) == 0) {
running = 0;
}
}

if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failedn");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}



使用 msgget 建立 message queue 每個 message queue 使用 key 值識別。

相同的 key 值的 message queue 在不同的 process 之間視為相同的 message queue

msg1.c

/* Here's the receiver program. */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>


struct my_msg_st {
long int my_msg_type;
char some_text[BUFSIZ];
};

int main()
{
int running = 1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive = 0;

/* First, we set up the message queue. */

msgid = msgget((key_t)1234, 0666 | IPC_CREAT);

if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %dn", errno);
exit(EXIT_FAILURE);
}

/* Then the messages are retrieved from the queue, until an end message is encountered.
Lastly, the message queue is deleted. */

while(running) {
if (msgrcv(msgid, (void *)&some_data, BUFSIZ,
msg_to_receive, 0) == -1) {
fprintf(stderr, "msgrcv failed with error: %dn", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s", some_data.some_text);
if (strncmp(some_data.some_text, "end", 3) == 0) {
running = 0;
}
}

if (msgctl(msgid, IPC_RMID, 0) == -1) {
fprintf(stderr, "msgctl(IPC_RMID) failedn");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

msg2.c

/* The sender program is very similar to msg1.c. In the main set up, delete the
msg_to_receive declaration and replace it with buffer[BUFSIZ], remove the message
queue delete and make the following changes to the running loop.
We now have a call to msgsnd to send the entered text to the queue. */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MAX_TEXT 512

struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};

int main()
{
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];

msgid = msgget((key_t)1234, 0666 | IPC_CREAT);

if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %dn", errno);
exit(EXIT_FAILURE);
}

while(running) {
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
some_data.my_msg_type = 1;
strcpy(some_data.some_text, buffer);

if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) {
fprintf(stderr, "msgsnd failedn");
exit(EXIT_FAILURE);
}
if (strncmp(buffer, "end", 3) == 0) {
running = 0;
}
}

exit(EXIT_SUCCESS);
}

Makefile

CROSS_COMPILE = arm-linux-

LINUXDIR = /usr/src/creator/s3c2410/linux
export LINUXDIR

#
# Include the make variables (CC, etc...)
#

AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar

export AS LD CC CPP AR

CFLAGS= -O0 -gdwarf-2 -DHAVE_CONFIG_H -DFPM_DEFAULT -Dlinux -Dunix -DNDEBUG -D_REENTRANT -I.

.c.o:
$(CC) $(CFLAGS) -c -o $@ $<

.S.o:
$(CC) $(AFLAGS) -c -o $@ $<

all: msg1 msg2 shm1 shm2 sem1

sem1.c: semun.h
shm1.c: shm_com.h
shm2.c: shm_com.h

msg1: msg1.o
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a

msg2: msg2.o
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a

shm1: shm1.o
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a

shm2: shm2.o
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a

sem1: sem1.o
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a

clean:
rm -f msg1 msg2 shm1 shm2 sem1

0 意見: