在Linux多进程编程的章节(7)中,我们深入探讨了共享进程资源的重要性。尽管这显著提高了系统的执行效率,但也带来了一些挑战。当多个进程同时读写共享内存时,数据安全性问题便浮出水面。正是为了解决这些问题,我们需要引入信号量的概念。
1.什么是信号量
信号量是多进程环境中使用的一种方法,用于实现进程间的互斥与同步,以保证它们能够正确、合理地使用公共资源。信号量是一个计数器,用于控制多个进程对共享资源的访问。常用的信号量函数包括:sem_open(), sem_close(),sem_wait(),sem_post(), sem_unlink(),将逐一进行介绍.
2.sem_open()
sem_open()
用于打开或创建一个命名信号量,其定义如下:
sem_t *sem_open(const char *name, int oflag, ...);
其中,name
代表信号量的名字;oflag
为打开标志,常用的有 O_CREAT
和 O_EXCL
。O_CREAT
表示如果名字的信号量不存在则创建它,而 O_EXCL
则在创建新信号量时使用,如果名字的信号量已经存在则返回错误。...
代表当使用 O_CREAT
标志时,用于指定信号量初始值的参数。如果没有使用 O_CREAT
,则此参数被忽略。
若函数执行成功,则返回一个指向信号量的指针;如果失败,返回 SEM_FAILED
。
3. sem_close()
sem_close()
用于关闭已打开的信号量,其定义如下:
int sem_close(sem_t *sem);
其中,sem
为指向已打开的信号量的指针。
若函数执行成功,返回 0,失败返回-1。
4.sem_wait()
sem_wait()
用于等待信号量,它会使调用进程等待,直到信号量的值大于零。其定义如下:
int sem_wait(sem_t *sem);
其中,sem
为指向信号量的指针。
若函数执行成功,返回 0,失败返回-1。
5.sem_post()
sem_post()
用于当一个进程完成对共享资源的访问后释放信号量,这样其他等待该信号量的进程就可以继续执行。其定义如下:
int sem_post(sem_t *sem);
其中,sem
为指向信号量的指针。
若函数执行成功,返回 0,失败返回-1。
6.sem_unlink()
sem_unlink()
用于当一个命名信号量不再需要时,将其从系统中删除。其定义如下:
int sem_unlink(const char *name);
其中,name
为信号量的名字。
若函数执行成功,返回 0,失败返回-1。
7.举例
目的:实现父进程和子进程操作共享内存,其中子进程负责写入数据"你好,新年快乐",父进程负责读出共享数据"你好,新年快乐";并通过信号量进行同步,确保在任何时候只有一个进程可以访问共享内存。
#include <stdio.h> // 用于标准输入输出
#include <stdlib.h> // 用于常用库函数,如内存分配
#include <string.h> // 用于字符串操作函数
#include <unistd.h> // 用于底层系统调用,如fork()
#include <sys/mman.h> // 用于内存映射和信号量等系统调用
#include <sys/wait.h> // 用于等待子进程
#include <semaphore.h> // 用于信号量操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
int main(){
int shm; // 共享内存标识符
key_t key; // 用于创建共享内存的键值
pid_t pid; // 进程ID
sem_t * sem=sem_open("/my_semaphore", O_CREAT, 0666, 1); // 创建或打开一个信号量,初始值为1
char * buff; // 指向共享内存的指针
key= ftok(".", 'R'); // 使用文件系统中的唯一键值创建键,用于创建共享内存对象
shm=shmget(key,1024,0666|IPC_CREAT); // 获取共享内存标识符,如果需要则创建共享内存
buff=(char*)shmat(shm,NULL,0); // 附加到共享内存并获取其地址
pid=fork(); // 创建子进程
if(pid<0){ // 如果fork()失败
perror("创建进程失败"); // 打印错误信息
}
else if(pid==0){ // 子进程代码块
printf("这是子进程\n");
sem_wait(sem); // 子进程等待信号量,确保父进程先执行
printf("子进程锁住资源\n");
strcpy(buff,"你好,新年快乐"); // 向共享内存写入数据
printf("%s\n",buff); // 打印共享内存中的数据
sem_post(sem); // 子进程释放信号量,允许其他进程继续执行
printf("子进程释放资源\n");
}
else{ // 父进程代码块
printf("这是父进程\n");
sleep(1); // 父进程等待1秒,确保子进程先执行
sem_wait(sem); // 父进程等待信号量,确保子进程已经执行完毕
printf("父进程锁住资源\n");
printf("%s\n",buff); // 打印共享内存中的数据
sem_post(sem); // 父进程释放信号量,允许其他进程继续执行
printf("父进程释放资源\n");
}
sem_close(sem); // 关闭信号量
sem_unlink("/my_semaphore"); // 删除信号量对象
shmdt(buff); // 断开共享内存的连接,使其可以被系统回收
shmctl(shm,IPC_RMID,NULL); // 删除共享内存对象
}
运行程序,得到结果: