Linux程序设计--14章(进程间通信)

Linux进程间信号
#include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semget(key_t key, int nsems, int semflg);
作用:创建一个新信号量或取得一个已有信号量的键
成功返回信号量标识符
key是一个整数值,不相关的进程可以通过它访问同一个信号量。其他信号量函数都使用semget返回的信号量标识符(间接使用);特殊键IPC_PRIVATE创建一个只有创建者进程才能访问的信号量
nsems指定需要信号量的数目
semflg标志,它的低9个比特是该信号的权限,类似于文件的访问权限(还可以与IPC_CREATE或,来创建一个信号量,即使已被创建,也不会出错;联合使用IPC_CREATE|IPC_EXCL来确保创建的是一个新的,唯一的信号量,如果已存在则出错)

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

       int semop(int semid, struct sembuf *sops, size_t nsops);
semid是semget返回的信号标识符
sembuf一般包含以下成员:sem_num是信号编号,一个信号情况下取0,sem_op为在一次操作中需要改变的值(P操作-1,V操作+1),sem_flg一般设置为SEM_UNDO操作系统会跟踪并在进程没有释放持有信号的情况下释放信号量

struct	sembuf{
	short sem_num;
	short sem_op;
	short sem_flg;
}
nsops操作的个数


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

       int semctl(int semid, int semnum, int cmd, ...);
semid是semget返回的信号标识符
sem_num是信号编号,一个信号情况下取0
cmd : SETVAL 用来把信号量初始化为一个已知的值
      IPC_RMID用于删除一个已经无需继续使用的信号量标识符
如果有第四个参数将会是一个union semun结构:
           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) */
           };

例子:
/* 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/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) {//如果参数个数大于1输出X
        if (!set_semvalue()) {//初始化信号量
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }
	//设置op_char=X
        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++) {        
//P操作
        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. */
//V操作
        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);
}

/* 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 semaphore\n");
}

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

static int semaphore_p(void)
{
    struct sembuf sem_b;
    //信号量-1(P操作)
    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);
}

/* 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)
{//信号量-1(V操作)
    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);
}
分别以带参数和不带参数两种情况调用,XXOO成对出现,因为P操作后保证没有执行到V操作时其他进程的P操作将等待


共享内存:
 #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmget(key_t key, size_t size, int shmflg);
key,有效的为共享内存段命名,shmget返回一个共享内存标识符,用于后续的共享内存函数(IPC_PRIVATE创建一个只属于创建进程的共享内存)
size 以字节为单位指定需要共享的内存容量
shmflg包含9个比特的权限标志,与创建文件时使用的mode标志一样(和IPC_CREATE按位或才能创建一个共享内存段)

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

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);
shmat将创建的共享内存连接到一个进程的地址空间。
shm_id是shmget返回的共享内存标识符
shmaddr指定的是共享内存连接到当前进程的地址位置(通常是一个空指针表示让系统来选择共享内存出现的地址)
shmflg是一组标志(SHM_RND和shmaddr联合使用来控制共享内存连接的地址,SHM_RDONLY使连接的内存只读)
调用成功返回一个指向共享内存第一个字节的指针,失败返回-1
共享内存的访问权限由它的属主它的访问权限和当前进程属主决定

shmdt函数的作用是将共享内存从当前进程中分离,它的参数是shmat返回的地址指针(并未删除)


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

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

struct shmid_ds {
              uid_t shm_perm.uid;
	      uid_t shm_perm.gid;
	      mode_t shm_perm.mode;
...
           };
shm_id是shmget返回的共享内存标识符
cmd是要采取的动作(IPC_STAT,IPC_SET,IPC_RMID删除共享内存)
buf是一个指针包含共享内存和访问权限的结构
例子:
消费者:


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.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());    
//创建共享内存或或取共享内存描述符,大小为定义的shared_use_st大小
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

    if (shmid == -1) {
        fprintf(stderr, "shmget failed\n");
        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 failed\n");
        exit(EXIT_FAILURE);
    }

    printf("Memory attached at %X\n", (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;
    //让共享内存written_by_you=0
    shared_stuff->written_by_you = 0;
    while(running) {
        if (shared_stuff->written_by_you) {//等待written_by_you=1
            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) {//end结束
                running = 0;
            }
        }
    }

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

    if (shmdt(shared_memory) == -1) {//分离共享内存
        fprintf(stderr, "shmdt failed\n");
        exit(EXIT_FAILURE);
    }

    if (shmctl(shmid, IPC_RMID, 0) == -1) {//删除共享内存
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}

生产者:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.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 failed\n");
        exit(EXIT_FAILURE);
    }
//连接到程序
    shared_memory = shmat(shmid, (void *)0, 0);
    if (shared_memory == (void *)-1) {
        fprintf(stderr, "shmat failed\n");
        exit(EXIT_FAILURE);
    }

    printf("Memory attached at %X\n", (int)shared_memory);
//转化成定义的数据结构
    shared_stuff = (struct shared_use_st *)shared_memory;
    while(running) {
        while(shared_stuff->written_by_you == 1) {//等待written_by_you=0
            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 failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}
消息队列:
#include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgget(key_t key, int msgflg);
key命名特定消息队列,IPC_PRIVATE用于创建私有队列
msgflg由9个权限标志组成(与IPC_CREATE按位或才能创建新的队列消息)
返回消息队列描述符

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

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);
消息结构:
           struct msgbuf {
               long mtype;       /*必须以长整形开头 */
               char mtext[1];    /* message data */
           };
msgsnd:0,-1
msqid是由msgget函数返回的消息队列标识符
msgp是指向一个准备发送消息的指针
msgsz是msgp的长度(不包含长长整形变量的长度)
msgflg控制在当前消息队列满或队列消息到达系统范围的限制时要发生的事情(IPC_NOWAIT函数立刻返回)

msgrcv:成功返回放到接收缓冲区的字节数,失败返回-1
msqid是由msgget函数返回的消息队列标识符
msgp是指向一个准备发送消息的指针
msgsz是msgp的长度(不包含长长整形变量的长度)
msgtyp是一个长整数,实现一种简单的优先级(=0按发送顺序接收,=正长整形接收某一特定消息,=-n接收类型等于或小于n的消息)
msgflg控制在当前消息队列满或队列消息到达系统范围的限制时要发生的事情(IPC_NOWAIT函数立刻返回)

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

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgqid_ds:
struct msqid_ds {
               uid_t msg_perm.uid;
	       uid_t msg_perm.gid;
	       mode_t msg_perm.mode;
           };
msqid是由msgget函数返回的消息队列标识符
cmd命令(IPC_STAT,IPC_SET,IPC_RMID删除消息队列)


例子:
发送者:


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.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: %d\n", 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 failed\n");
            exit(EXIT_FAILURE);
        }
        if (strncmp(buffer, "end", 3) == 0) {
            running = 0;
        }
    }

    exit(EXIT_SUCCESS);
}

接收者:


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.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;

//获取消息队列标识符
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);

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


    while(running) {
	    //接收msg_to_receive消息的数据,并转成定义的结构
        if (msgrcv(msgid, (void *)&some_data, BUFSIZ,
                   msg_to_receive, 0) == -1) {
            fprintf(stderr, "msgrcv failed with error: %d\n", 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) failed\n");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}

发送者可在接收者启动之前就退出
这里不需要提供同步方法

猜你喜欢

转载自blog.csdn.net/yanshaoshuai/article/details/81085107