1、内核对进程访问的管理
(1)、进程在访问共享资源是存在冲突的,必须的有一种强制手段说明这些共享资源的访问规则。
访问规则的原理:当有一个进程在访问这些共享资源的时候,需要明确的向其他的进程表示:该资源已经被占用。
(2)、访问规则
现代操作系统一定是多进程的(多任务),多进程的环境一定存在共享资源的访问冲突问题。操作系统提供了一种机制对"访问规则的原理"进行的实现。
i>、用一种数量去标识某一种共享资源的个数(空闲)。
ii>、当有进程需要访问对应的共享资源的时候,则需要先查看(申请),根据资源对应的当前可用数量进行申请。(申请所需要使用的资源个数)
iii>、资源的管理者(操作系统内核),就使用当前的资源个数减去要申请的资源个数,结果>=0,表示有可用资源,允许该进程继续访问;否则表示资源不可用,则告诉进程(暂停或者立即返回);
iv>、资源数量的变化就表示资源的占用和释放。占用:使得可用资源减少;释放:使得可用资源增加。
2、信号量的编程
这种表示资源个数的处理共享资源访问规则的方式------>信号量。
(1)、创建/获取信号量
Linux为了方便进程对于信号量的编程操作,通过一个key创建的一个信号量并非只是一个信号量,而是一个信号量集合。
为了方便进程间信号量的管理,在Linux的内核设计中,对于信号量的申请(创建),一个key值可以创建一组信号量。就可以使得进程间是约定一个key值,而控制一组共享资源的访问。
需要使用API:semget()方法
int semget(key_t key, int nsems, int semflg);
其中nsems参数表示此次创建的信号量集合中信号量的个数(表示有多少种共享资源需要管理)。该数字大于0,小于系统规定的最大值,该最大值的描述存放在文件/etc/sysctl.conf。
(2)、初始化信号量
信号量ID事实上是信号量集合的ID,一个ID对应的是一组信号量。此时就使用信号量ID设置整个信号量集合,这种操作分为2种大的可能性。
i>、针对信号量集合中的一个信号量进行设置;信号量集合中的信号量是按照数组的方式被管理起来的,从而可以直接使用信号的数组下标来进行访问。
ii>、针对整个信号量集和进行统一的设置。
需要使用的API:semctl()方法。
int semctl(int semid, int semnum, int cmd, ...);
cmd参数
GETALL | 获取信号量集合中所有信号量的资源个数 |
SETALL | 设置所有 |
GETVAL | 获取其中一个 |
SETVAL | 设置其中一个 |
第四个参数 可变参
如果cmd是GETALL、SETALL、GETVAL、SETVAL...的话,则需要提供第四个参数。第四个参数是一个共用体,这个共用体在程序中必须的自己定义(作用:初始化资源个数),定义格式如下:
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) */ };
(3)、信号量的操作
需要使用的API:semop()方法。 (op:operator操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);
第二个参数需要借助结构体struct sembuf:
struct sembuf{ unsigned short sem_num; /* semaphore number 数组下标 */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags 默认0*/ };
第三个参数一般情况下为1。
(4)、控制信号量
信号量控制:信号量集合中各个信号量的资源个数的设置与获取。
3、信号量的特征
如果有进程通过信号量申请共享资源,而且此时资源个数已经小于0,则此时对于该进程,有两种可能性:等待资源,不等待。
如果此时进程选择等待资源,则操作系统内核会针对该信号量构建进程等待队列,将等待的进程加入到该队列之中。
如果此时有进程释放资源,则会:(1)、先将资源个数增加;(2)、从等待队列中抽取第一个进程;(3)、根据此时资源个数和第一个进程需要申请的资源个数进行比较,结果大于0,则唤醒该进程;结果小于0,则让该进程继续等待。
4、编程实现
实现一个简单的运用信号量的程序:
#include<stdio.h> #include<unistd.h> #include<sys/ipc.h> #include<sys/sem.h> 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) */ }; int main(void){ key_t sem_key; sem_key = ftok("test", 7); //动态获取key; if(sem_key == -1){ perror("ftok"); return -1; } printf("sem_key = %x\n",sem_key); int sem_id = semget(sem_key, 1, IPC_CREAT|IPC_EXCL|0755); //创建的共享资源数为1个 if(sem_id == -1){ perror("semget"); return -1; } printf("sem_id = %d\n",sem_id); union semun init; init.val = 10; int res = semctl(sem_id, 0, SETVAL, init); //数组下标为0的 if(res == -1){ perror("semctl setval"); return -1; } int sem_val = semctl(sem_id, 0, GETVAL); //10 printf("sem_val = %d\n",sem_val); struct sembuf info; info.sem_num = 0; info.sem_op = -1; //减少资源 info.sem_flg = 0; semop(sem_id, &info, 1); //1:代表针对1个信号量资源 sem_val = semctl(sem_id, 0, GETVAL); //9 printf("sem_val = %d\n",sem_val); res = semctl(sem_id, 0, IPC_RMID); if(res == -1){ perror("semctl rmid"); return -1; } printf("Remove Sem OK.\n"); return 0; }
运行结果