今天迎来元旦假期的最后一天了,过得好快~昨天跟小伙伴们在军都滑雪陪儿爽,虽说上了两回中级道都摔得异常的惨烈,但是在初级道上学习"s"转弯还是有一些小心得,可以在要往高手迈进的前提,一定得要把基本功打扎实,否则会很惨烈~好了,在这无聊的下午,用博客继续充实自己。

上次学习了System v 信号量的一些概念,并封装了一些常用方法,下面会举例用信号量来实现进程互斥,来进一步加深对信号量的认识。

先用图来描述一下这个程序的一个意图:

下面则开始实现,基于之前信号量的封装:

print.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.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) */
}; #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int sem_create(key_t key)
{
int semid;
semid = semget(key, , IPC_CREAT | IPC_EXCL | );
if (semid == -)
ERR_EXIT("semget"); return semid;
} int sem_open(key_t key)
{
int semid;
semid = semget(key, , );
if (semid == -)
ERR_EXIT("semget"); return semid;
} int sem_setval(int semid, int val)
{
union semun su;
su.val = val;
int ret;
ret = semctl(semid, , SETVAL, su);
if (ret == -)
ERR_EXIT("sem_setval"); return ;
} int sem_getval(int semid)
{
int ret;
ret = semctl(semid, , GETVAL, );
if (ret == -)
ERR_EXIT("sem_getval"); return ret;
} int sem_d(int semid)
{
int ret;
ret = semctl(semid, , IPC_RMID, );
if (ret == -)
ERR_EXIT("semctl"); return ;
} int sem_p(int semid)
{
struct sembuf sb = {, -, };
int ret;
ret = semop(semid, &sb, );
if (ret == -)
ERR_EXIT("semop"); return ret;
} int sem_v(int semid)
{
struct sembuf sb = {, , };
int ret;
ret = semop(semid, &sb, );
if (ret == -)
ERR_EXIT("semop"); return ret;
} int semid; int main(int argc, char *argv[])
{
semid = sem_create(IPC_PRIVATE);//由于是父子进程,所以可以创建私有的信号量集
sem_setval(semid, );//初始化信号量计数值为0
pid_t pid;
pid = fork();
if (pid == -)
ERR_EXIT("fork"); if (pid > )
{//父进程
}
else
{//子进程
}
return ;
}

接下来则进行值打印:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.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) */
}; #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int sem_create(key_t key)
{
int semid;
semid = semget(key, , IPC_CREAT | IPC_EXCL | );
if (semid == -)
ERR_EXIT("semget"); return semid;
} int sem_open(key_t key)
{
int semid;
semid = semget(key, , );
if (semid == -)
ERR_EXIT("semget"); return semid;
} int sem_setval(int semid, int val)
{
union semun su;
su.val = val;
int ret;
ret = semctl(semid, , SETVAL, su);
if (ret == -)
ERR_EXIT("sem_setval"); return ;
} int sem_getval(int semid)
{
int ret;
ret = semctl(semid, , GETVAL, );
if (ret == -)
ERR_EXIT("sem_getval"); return ret;
} int sem_d(int semid)
{
int ret;
ret = semctl(semid, , IPC_RMID, );
if (ret == -)
ERR_EXIT("semctl"); return ;
} int sem_p(int semid)
{
struct sembuf sb = {, -, };
int ret;
ret = semop(semid, &sb, );
if (ret == -)
ERR_EXIT("semop"); return ret;
} int sem_v(int semid)
{
struct sembuf sb = {, , };
int ret;
ret = semop(semid, &sb, );
if (ret == -)
ERR_EXIT("semop"); return ret;
} int semid; void print(char op_char)
{
int pause_time;
srand(getpid());//以当前进程做为随机数的种子
int i;
for (i=; i<; i++)//各输出十次
{
sem_p(semid);//进行一个P操作
printf("%c", op_char);
fflush(stdout);//由于没有用\n,所以要想在屏幕中打印出字符,需要强制清空一下缓冲区
pause_time = rand() % ;//在0,1,2秒中随机
sleep(pause_time);
printf("%c", op_char);
fflush(stdout);
sem_v(semid);//进行一个V操作
pause_time = rand() % ;
sleep(pause_time);//最后在0,1秒中随机
}
} int main(int argc, char *argv[])
{
semid = sem_create(IPC_PRIVATE);//由于是父子进程,所以可以创建私有的信号量集
sem_setval(semid, );//初始化信号量计数值为0
pid_t pid;
pid = fork();
if (pid == -)
ERR_EXIT("fork"); if (pid > )
{//父进程
sem_setval(semid, );//由于计数值初使为0,所以进行P操作时则会等待,为了进行p操作,则设置值为1
print('O');
wait(NULL);//等待子进程的退出
sem_d(semid);//最后删除信号量值
}
else
{//子进程
print('X');
}
return ;
}

下面编译运行来看一下效果:

从运行结果来看,o跟x一定是成对出现的,不可能出现ox一起打印,这就是信号量达到互斥作用的效果。

接下来用信号集来解决哲学家就餐问题,而且这一次信号集中信号量的个数不再是1个,而是多个,关于哲学家就餐问题可参考:http://www.cnblogs.com/webor2006/p/4149323.html,怎么解决呢?

下面回归到实际代码上来,由于这次的信号集中有多个信号量,所以这个实验中就不能用之前封装的方法了,需重新编写:

dining.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.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) */
}; #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int semid; int main(int argc, char *argv[])
{
semid = semget(IPC_PRIVATE, , IPC_CREAT | );//创建一个信号量集,里面包含五个信号量,这里也用私有的方式,因为会用子进程的方式模拟
if (semid == -)
ERR_EXIT("semget"); //将五个信号量的计数值都初始为1,资源都可用,模拟的是五把叉子
union semun su;
su.val = ;
int i;
for (i=; i<; i++)
{
semctl(semid, i, SETVAL, su);
} return ;
}

而哲学家所做的事情如下:

具体实现如下:

接下来则实现wait_for_2fork()、free_2fork()两个函数:

结合图来想,就很容易明白这个算法,如下:

同样的,释放叉子类似:

至此解决哲学家就餐问题的代码就写完,下面来编译运行一下:

从中可以看到,没有出现死锁问题,下面从输出结果来分析一下:

从结果分析来看:不可能两个相邻的哲学家同时处于“吃”的状态,同时只能够有两个哲学家处于“吃”的状态。

接下来再来模拟一下死锁的情况,在模拟之前,注意:需手动将创建的信号量集给删掉,因为刚才运行是强制关闭程序的,另外在实现之前,需要思考一下怎么样能产生死锁,其实思路很简单,就是申请叉子的时候,一个个申请,而不是当只有两个都有的情况下才能申请,所以,修改代码如下:

接下来实现wait_1fork():

下面编译运行:

从结果来看确实是阻塞了,由于都拿起了左边的叉子,而且都在等待右边叉子,而都没人释放左叉子,于是乎死锁就产生了。

最后贴上完整代码:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.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) */
}; #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() #define DELAY (rand() % 5 + 1)//定义一个睡眠时间,1~5秒中
int semid; //等待一把叉子
int wait_1fork(int no)
{
struct sembuf sb = {no, -, };
int ret;
ret = semop(semid, &sb, );
if (ret == -)
ERR_EXIT("semop"); return ret;
} //等待左右两个叉子
void wait_for_2fork(int no)
{
int left = no;
int right = (no + ) % ; struct sembuf buf[] = {
{left, -, },
{right, -, }
}; semop(semid, buf, );
} //释放左右两信叉子
void free_2fork(int no)
{
int left = no;
int right = (no + ) % ; struct sembuf buf[] = {
{left, , },
{right, , }
}; semop(semid, buf, );
} void philosophere(int no)
{
srand(getpid());//设置随机的种子
for (;;)
{//不断循环执行
/*
printf("%d is thinking\n", no);//首先思考
sleep(DELAY);
printf("%d is hungry\n", no);//饿了
wait_for_2fork(no);
printf("%d is eating\n", no);//当获取到了左右两把叉子,则开吃
sleep(DELAY);
free_2fork(no);//吃完则放下左右两把叉子
*/ int left = no;
int right = (no + ) % ;
printf("%d is thinking\n", no);
sleep(DELAY);
printf("%d is hungry\n", no);
wait_1fork(left);
sleep(DELAY);
wait_1fork(right);
printf("%d is eating\n", no);
sleep(DELAY);
free_2fork(no);
}
} int main(int argc, char *argv[])
{
semid = semget(IPC_PRIVATE, , IPC_CREAT | );//创建一个信号量集,里面包含五个信号量,这里也用私有的方式,因为会用子进程的方式模拟
if (semid == -)
ERR_EXIT("semget"); //将五个信号量的计数值都初始为1,资源都可用,模拟的是五把叉子
union semun su;
su.val = ;
int i;
for (i=; i<; i++)
{
semctl(semid, i, SETVAL, su);
} //接下来创建四个子进程,加上父进程则为5个,来模拟5个哲学家
int no = ;
pid_t pid;
for (i=; i<; i++)
{
pid = fork();
if (pid == -)
ERR_EXIT("fork"); if (pid == )
{
no = i;
break;
}
} philosophere(no); return ;
}

好了,今天学到这,肚子饿了,吃饭下次继续~

linux网络编程之system v信号量(二)的更多相关文章

  1. linux网络编程之system v信号量(一)

    今天起,学习信号量相关的知识,下面开始: 关于信号量,在前面已经介绍过了,这里回顾一下: 通过上面的描述,很容易就能想到信号量的一上数据结构: 下面再来回顾一下P.V原语: 所谓的原语就是指这段代码是 ...

  2. linux网络编程之system v消息队列(二)

    今天继续学习system v消息队列,主要是学习两个函数的使用,开始进入正题: 下面则开始用代码来使用一下该发送函数: 在运行之前,先查看一下1234消息队列是否已经创建: 用上次编写的查看消息队列状 ...

  3. linux网络编程之system v共享内存

    接着上次的共享内存继续学习,这次主要是学习system v共享内存的使用,下面继续: 跟消息队列一样,共享内存也是有自己的数据结构的,system v共享内存也是随内核持续的,也就是说当最后一个访问内 ...

  4. linux网络编程之system v消息队列(一)

    经过上次对于进程通讯的一些理论的认识之后,接下来会通过实验来进一步加深对进程通讯的认识,话不多说,进入正题: 其实还可以通过管道,但是,管道是基于字节流的,所以通常会将它称为流管道,数据与数据之间是没 ...

  5. linux网络编程之socket编程(十二)

    今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...

  6. linux网络编程之posix线程(二)

    继续接着上次的posix线程来学习: 回顾一下创建线程的函数: pthread_att_t属性变量是需要进行初始化才能够用的,一定初始化了属性变量,它就包含了线程的多种属性的值,那到底有哪些属性了,下 ...

  7. linux网络编程之shutdown() 与 close()函数详解

    linux网络编程之shutdown() 与 close()函数详解 参考TCPIP网络编程和UNP: shutdown函数不能关闭套接字,只能关闭输入和输出流,然后发送EOF,假设套接字为A,那么这 ...

  8. Linux IPC实践(11) --System V信号量(1)

    信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget ...

  9. linux网络编程之socket编程(四)

    经过两周的等待,终于可以回归我正常的学习之旅了,表哥来北京了在我这暂住,晚上回家了基本在和他聊天,周末带他在北京城到处乱转,几乎剥夺了我自由学习的时间了,不过,亲人之情还是很难得的,工作学习并不是生活 ...

随机推荐

  1. web端自动化——Selenium3+python自动化(3.7版本)-火狐62版本环境搭建

    前言 目前selenium版本已经升级到3.0了,网上的大部分教程是基于2.0写的,所以在学习前先要弄清楚版本号,这点非常重要.本系列依然以selenium3为基础. 一.selenium简介 Sel ...

  2. 深入css过渡transition

    通过过渡transition,可以让web前端开发人员不需要javascript就可以实现简单的动画交互效果.过渡属性看似简单,但实际上它有很多需要注意的细节和容易混淆的地方. 过渡transitio ...

  3. 方法重载overload与方法重写overwrite

    方法重载overload: 在同一个类中,出现相同的方法名,与返回值无关,参数列表不同:1参数的个数不同 2参数类型不同 在调用方法时,java虚拟机会通过参数列表来区分不同同名的方法 方法重写ove ...

  4. pytorch1.0实现AutoEncoder

    AutoEncoder (自编码器-非监督学习)神经网络也能进行非监督学习, 只需要训练数据, 不需要标签数据. 自编码就是这样一种形式.自编码能自动分类数据, 而且也能嵌套在半监督学习的上面, 用少 ...

  5. Javascript 闭包何时回收?

    定义 闭包是函数和声明该函数的词法环境的组合.闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量. 范例 fun ...

  6. 八皇后问题——列出所有的解,可推至N皇后

    <数据结构>--邓俊辉版本 读书笔记 今天学习了回溯法,有两道习题,一道N皇后,一道迷宫寻径.今天,先解决N皇后问题.由于笔者 擅长java,所以用java重现了八皇后问题. 注意是jav ...

  7. PAT甲级 链表题_C++题解

    链表处理 PAT (Advanced Level) Practice 链表题 目录 <算法笔记> 重点摘要:静态链表 1032 Sharing (25) 1052 Linked List ...

  8. 简单layer 快速上手

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. PAT(B) 1074 宇宙无敌加法器(Java)

    题目链接:1074 宇宙无敌加法器 (20 point(s)) 题目描述 地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的.而在 PAT 星人开挂的世界里,每个数字的每一位都是不同进制的, ...

  10. NodeJS入门--环境搭建 IntelliJ IDEA

    NodeJS入门–环境搭建 IntelliJ IDEA 本人也刚开始学习NodeJS,所以以此做个笔记,欢迎大家提出意见. 1.首先 下载安装NodeJS,下载安装IntelliJ IDEA 2.接下 ...