OS实验:进程管理与死锁
进程管理与死锁
- 一. 实验目的
- 二. 实验内容
- 三. 实验过程
- 3.1 在Linux 下创建一对父子进程
- 让父进程提前结束
- 让父进程后结束
- 3.2 在Linux下创建两个线程A和B,循环输出数据或字符串
- 3.3 在Linux下创建父子进程,实验wait同步函数,理解父子进程同步
- 3.4 在Linux下利用线程实现“生产者-消费者”同步控制
- 3.4.1 使用信号量
- 3.4.2 使用条件变量+互斥锁
- 3.5 在Linux下利用信号机制实现进程通信
- 3.5.1 信号注册
- 3.5.2 信号发送
- 3.5.3 信号类型
- 3.5.4 代码实现
- 3.6 在Linux 下模拟哲学家 就餐,提供死锁和非死锁解法
- 信号量实现的死锁解法
- 信号量实现非死锁解法
- 条件变量+互斥锁
- 参考资料:
一. 实验目的
- 理解进程/线程的概念和应用编程过程
- 理解进程/线程的同步机制和应用编程
二. 实验内容
- 在Linux下创建一队父子进程
- 在Linux下创建两个线程A和B,循环输出数据或字符串
- 在Linux下创建父子进程,实验wait同步函数,理解父子进程同步
- 在Linux下利用线程实现“生产者-消费者”同步控制
- 在Linux 下模拟哲学家 就餐,提供死锁和非死锁解法
三. 实验过程
3.1 在Linux 下创建一对父子进程
- 分别输出各自的进程号,父进程号和特别的提示字符串信息
- 让父进程提前结束或后结束,观察子进程的父进程ID
- 使用PS命令查看进程列表信息,核对进程号,父进程号
要创建子进程,需要用到 fork() 函数,该函数的函数原型声明在 unistd.h 中
pid_t fork(void);
返回值:
如果成功,父进程中,将返回子进程的pid(pid>0);子进程中,将返回0 (pid =0)。
如果失败,父进程中,会返回-1,而子进程不会被创建。并且设置errno的值来指出这个错误。
由 fork 创建的新进程被称为子进程。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。子进程可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。
fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置
创建一个c文件 使用命令vim fork.c
,输入以下代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>void main()
{pid_t pid;fprintf(stdout, "I am the first process, my pid is %d, my parent pid is %d\n", getpid(), getppid());pid = fork();if (0 == pid)fprintf(stdout, "I am the child procress, my pid value is %d, but my real pid is %d, my parent pid is %d\n", pid, getpid(), getppid());elsefprintf(stdout, "I am the parent process, my child pid is %d, my pid is %d, my parent pid is %d\n", pid, getpid(), getppid());// 让该线程睡眠一会,从而使用ps 命令查看线程时,该线程没有结束// 睡眠50ssleep(50);
}
编译gcc -o fork.out fork.c
输出结果
我们运行的 ./fork.out 本身也是一个进程,它的pid是5833,ppid是2765(它的parent是bash)
使用fork()函数后创建的子进程的pid是5834,ppid是5833
使用命令ps -ef
第二列是pid,第三列是ppid(父进程的pid)。可以发现进程号和父进程号是符合的。
为了观察 让父进程提前结束或后结束,观察子进程的父进程ID
让父进程提前结束
修改 fork.c文件(也可以再创建一个)
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[]) {printf("start program %s, pid: %d, ppid: %d \n", argv[0], getpid(), getppid());pid_t pid = fork();if (-1 == pid) {printf("fork process failed. errno: %u, error: %s\n", errno, strerror(errno));exit(-1);} if (pid > 0) { // parent printf("parent process\n");sleep(1);
/** 标记一、wait 等待子进程结束*/
// int status;
// wait(&status);} else { // child printf("child process, pid: %d, ppid: %d\n", getpid(), getppid());for (int i = 0; i < 5; i++) {printf("child sleep %ds\n", i); sleep(1);} printf("##child process, pid: %d, ppid: %d\n", getpid(), getppid());} return 0;
}
再次编译 gcc -o fork.out fork.c
运行./fork.out
输出结果
发现父线程先关闭后,子线程的ppid由6040 变为了 1892
1892对应命令 /lib/systemd/systemd/ --user 代表systemd进程,这里有人可能会疑惑为什么不是init进程(ppid不是1)。
其实systemd 是 init 的现代替代品。绝大多数linux发行版(ubuntu, fedora, centos, arch 等等)已经用systemd替换了传统的init。systemd 担任的角色非常多,其中之一负责管理用户会话,也就是作为用户图形界面的父进程进行管理。systemd --user 会调用 prctl,接替全局的init进程的职责,处理所有孤儿进程。
如果在tty界面下实验,可以按ctrl + alt + F3
打开tty界面,再次运行该程序,发现父进程先结束后,子进程的ppid变为了符合预期的1.也就是被init进程接管了。 之后可以使用ctrl + alt +F2
返回图形界面。
让父进程后结束
把“标志一”的wait代码删除注释,使用以下代码
/** 标记一、wait 等待子进程结束 */int status;wait(&status);
同样步骤编译运行后结果显示为
wait函数的作用是等待子进程结束,并获取子进程结束的状态,所以父进程wait等待子进程结束后才继续运行退出,故子进程的ppid一直是原来fork它的父进程的pid,一直是6232.
3.2 在Linux下创建两个线程A和B,循环输出数据或字符串
- 使用pthread线程库
- 线程A递增输出1-1000; 线程B递减输出1000-1。为避免输出太快,每隔0.2s(可自行调节)输出一个数。
- 输出数据时,同时输出A或B以标识是哪个线程输出的,并注意格式化输出信息。
线程相关函数 pthread_xxx 都在头文件 pthread.h里。
函数 pthread_create():用于创建线程
int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void *),void *restrict arg);
- thread: 返回的线程句柄
- attr : 指定线程属性
- start_routine : 线程函数入口地址
- arg : 线程函数参数
函数 pthread_join: 用于等待目标线程终止
int pthread_join(pthread_t thread, void **retval);
- thread :目标线程
- retval :NULL即可
创建一个c文件(如trial2.c),并输入以下代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <errno.h> // sleep xx毫秒
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){errno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);return res;
}
//打印 1~1000
void * print1()
{int i = 0;while (i <= 1000){printf("A:%d\n", i);i++;// 间隔0.2秒msleep(200);}
}//打印1000~1
void * print2()
{int i = 1000;while (i > 0){printf("B:%d\n", i);i--;// 间隔0.2秒msleep(200);}
}int main()
{pthread_t thread1;pthread_t thread2;int hThread1 = pthread_create(&thread1, NULL, print1, NULL);int hThread2 = pthread_create(&thread2, NULL, print2, NULL);if (hThread1 != 0){printf("hThread1 err");}else if (hThread2 != 0){printf("hThread2 err!");}pthread_join(thread1, NULL); // pthread_join(thread2, NULL); // return 0;
}
msleep() 函数参考Is there an alternative sleep function in C to milliseconds?
输入指令gcc -o trial2.out trial2.c -pthread
进行编译,再输入./trial2.out
运行
其中注意是 -pthread 而不是 -plthread,解释可见Undefined reference to pthread_create in Linux和Compiling Threaded Programs
可参考以下表格
3.3 在Linux下创建父子进程,实验wait同步函数,理解父子进程同步
- 子进程休眠5秒,父进程不休眠。子进程用exit返回参数
- 父进程调用wait等待子进程先结束,并分析子进程返回参数
- 父进程输出子进程的返回信息
wait函数 :用于等待一个子进程停止或终止(随机的一个子进程)
#include <sys/wait.h>pid_t wait(int *stat_loc);
- stat_loc: 存储子进程结束时的返回值
创建新文件(如trial3.c),并且输入以下代码
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[]) {printf("start program %s, pid: %d, ppid: %d \n", argv[0], getpid(), getppid());pid_t pid = fork();if (-1 == pid) {printf("fork process failed. errno: %u, error: %s\n", errno, strerror(errno));exit(-1);} if (pid > 0) { // parent int status;wait(&status);printf("child process return %d\n",status);} else { // child // 子进程休眠5ssleep(5);// 子进程返回 0exit(0);} return 0;
}
使用命令gcc -o trial3.out trial3.c -pthread
编译,再使用命令./trial3.out
运行
显示以下结果,可以发现父进程输出了子进程的返回信息 0
3.4 在Linux下利用线程实现“生产者-消费者”同步控制
- 使用数组(10个元素) 代替缓冲区。2个输入线程产生产品(随机数)存到数组中;3个输出线程从数组中取数输出
- Linux使用 互斥锁对象 和 轻量级信号对象 ,主要函数 :sem_wait() ,sem_post() ,pthread_mutex_lock() ,pthread_mutex_unlock()
- 生产者1的数据: 1000-1999(每个数据随机间隔100ms-1s),生产者2的数据: 2000-2999(每个数据随机间隔100ms-1s)
- 消费者每休眠100ms-1s的随机时间消费一个数据
- 屏幕打印(或日志文件记录)每个数据的生产和消费记录
3.4.1 使用信号量
信号量相关函数 在头文件 semaphore.h中
sem_init():动态初始化一个信号量
#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);
- sem :想要初始化的信号量
- pshared :表明信号量是进程间共享,还是一个进程内的线程共享。若为0,则,一个进程内的线程共享,否则进程间共享。
- value: 信号量初始值
sem_wait : 即P操作
int sem_wait(sem_t *sem);
- sem : 指定操作的信号量
sem_post :即V操作
int sem_post(sem_t *sem);
- sem : 指定操作的信号量
sem_destory :销毁指定信号量
int sem_destroy(sem_t *sem);
- sem : 指定操作的信号量
创建文件(如trial4.c),输入以下代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<time.h>
#include <errno.h> #define BUFFER_SIZE 10 // size of buffer// p_sem : produce semaphore
// indicate that the number of producer threads that can run at the same time
// == unfilled buffer space// c_sem : consume semaphore
// indicare that the number of consumer threads that can run at the same time
// == filled buffer space
sem_t p_sem, c_sem;// s is used to achieve mutex
sem_t s;
// declare buffer
int buffer[BUFFER_SIZE];//
int in =0,out = 0;int msleep(long msec);
void delay();
void * producer1(void *arg);
void * producer2(void *arg);
void * consumer(void *arg);int main()
{// init semaphore p_sem and c_semsem_init(&p_sem,0,BUFFER_SIZE);sem_init(&c_sem,0,0);// init semaphore ssem_init(&s,0,1);srand((unsigned)time(NULL));pthread_t pro1,pro2;pthread_t con1,con2,con3;pthread_create(&pro1,NULL,producer1,NULL);pthread_create(&pro2,NULL,producer2,NULL);pthread_create(&con1,NULL,consumer,NULL);pthread_create(&con2,NULL,consumer,NULL);pthread_create(&con3,NULL,consumer,NULL);pthread_join(pro1,NULL);pthread_join(pro2,NULL);pthread_join(con1,NULL);pthread_join(con2,NULL);pthread_join(con3,NULL);sem_destroy(&p_sem);sem_destroy(&c_sem);sem_destroy(&s);return 0;
}// produce(consume) delay
void delay()
{long interval = rand()%901+100;msleep(interval);
}void * producer1(void *arg) //生产者线程
{//printf("producer1");int data;while(1){// printf("producer1xxx");// produce date// produce1(); data = rand()%1000 + 1000;printf("produce data %d\n",data);sem_wait(&p_sem);//生产信号量减一sem_wait(&s);// put the data(v) item into the bufferbuffer[in] = data;in = (in + 1) % BUFFER_SIZE ;sem_post(&s);// consume semaphore plus onesem_post(&c_sem);delay();}
}void * producer2(void *arg) //生产者线程
{//printf("producer2\n");int data;while(1){//printf("producer2xxx\n");// produce data// produce2(); data = rand()%1000 + 2000;printf("produce data %d\n",data);sem_wait(&p_sem);//生产信号量减一sem_wait(&s);// put the data(v) item into the bufferbuffer[in] = data;in = (in + 1) % BUFFER_SIZE ;sem_post(&s);// consume semaphore plus onesem_post(&c_sem);delay();}
}void * consumer(void *arg) //消费者线程
{//printf("consumer");int data;while(1){sem_wait(&c_sem); //消费者信号量减一 sem_wait(&s); data= buffer[out];out = (out +1) % BUFFER_SIZE;sem_post(&s);sem_post(&p_sem);//生产者信号量加一 //consum();printf("consume data %d\n",data);delay();}
}// sleep thread 'msec' millisecond
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){// EINVAL : Invalid argumenterrno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);// EINTR: Interrupted function call// when Interruption of system calls and library functions return res;
}
使用命令gcc -o trial4.out trial4.c -pthread
编译,再使用命令./trial4.out
运行
3.4.2 使用条件变量+互斥锁
函数pthread_cond_init() : 初始化一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化
- cond:想要初始化的条件变量的地址
- attr :指明条件变量的属性。若为NULL,则使用默认属性。
函数pthread_cond_destroy() :销毁一个条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
- cond: 想要销毁的条件变量的地址
函数pthread_cond_signal: 唤醒一个阻塞在条件变量的线程
int pthread_cond_signal(pthread_cond_t *cond);
- cond: 指定条件变量的地址
函数pthread_cond_wait:阻塞调用该函数的线程,并释放指定互斥锁
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
- cond : 指定条件变量的地址
- mutex : 指定的互斥锁
函数pthread_mutex_init:初始化一个互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化,默认属性
- mutex:想要初始化的互斥锁的地址
- attr :指明互斥锁的属性。若为NULL,则使用默认属性。
函数pthread_mutex_destroy:销毁一个互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- mutex: 指定互斥锁的地址
函数pthread_mutex_lock:获取锁
函数pthread_mutex_unlock:释放锁
int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);
- mutex : 指定互斥锁的地址
代码如下
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<time.h>
#include <errno.h> #define BUFFER_SIZE 10 // size of buffer
#define PRODUCER_NUM 2// declare buffer
int buffer[BUFFER_SIZE];//
int in =0,out = 0;pthread_mutex_t lock;
pthread_cond_t consume_cond,produce_cond;int msleep(long msec);
void delay();
void * producer(void *arg);
void * consumer(void *arg);int main()
{pthread_mutex_init(&lock, 0);pthread_cond_init(&consume_cond,0);pthread_cond_init(&produce_cond,0);srand((unsigned)time(NULL));pthread_t pro1,pro2;pthread_t con1,con2,con3;int producer_args[PRODUCER_NUM] = {1000,2000};pthread_create(&pro1,NULL,producer,producer_args);pthread_create(&pro2,NULL,producer,(producer_args+1));pthread_create(&con1,NULL,consumer,NULL);pthread_create(&con2,NULL,consumer,NULL);pthread_create(&con3,NULL,consumer,NULL);pthread_join(pro1,NULL);pthread_join(pro2,NULL);pthread_join(con1,NULL);pthread_join(con2,NULL);pthread_join(con3,NULL);pthread_mutex_destroy(&lock);pthread_cond_destroy(&consume_cond);pthread_cond_destroy(&produce_cond);return 0;
}// produce(consume) delay
void delay()
{long interval = rand()%901+100;msleep(interval);
}void * producer(void *arg) //生产者线程
{int range = *(int *) arg;//printf("producer1");int data;while(1){// produce date// produce1(); data = rand()%1000 + range;printf("produce data %d\n",data);pthread_mutex_lock(&lock);while((in+1)%BUFFER_SIZE == out){pthread_cond_wait(&produce_cond, &lock);}// put the data(v) item into the bufferbuffer[in] = data;in = (in + 1) % BUFFER_SIZE ;pthread_cond_signal(&consume_cond);pthread_mutex_unlock(&lock);delay();}
}void * consumer(void *arg) //消费者线程
{//printf("consumer");int data;while(1){// sem_wait(&c_sem); //消费者信号量减一 // sem_wait(&s); // data= buffer[out];// out = (out +1) % BUFFER_SIZE;// sem_post(&s);// sem_post(&p_sem);//生产者信号量加一 pthread_mutex_lock(&lock);while(in == out){pthread_cond_wait(&consume_cond,&lock);}data= buffer[out];out = (out +1) % BUFFER_SIZE;pthread_cond_signal(&produce_cond);pthread_mutex_unlock(&lock);//consum();printf("consume data %d\n",data);delay();}return NULL;
}// sleep thread 'msec' millisecond
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){// EINVAL : Invalid argumenterrno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);// EINTR: Interrupted function call// when Interruption of system calls and library functions return res;
}
3.5 在Linux下利用信号机制实现进程通信
- 父进程创建子进程,并让子进程进入死循环
- 子进程每隔2秒输出“I am Child Process,alive!\n"
- 父进程询问用户”To terminate Child Process.Yes or No?"
- 若用户回答Y,向子进程发送 用户信号 ,让子进程结束
- 函数:kill(),signal(),利用用户信号,编写信号处理函数
关于信号机制,可参考signale(2) 、signale(7)和Linux 信号(signal)
现在简单说一下信号机制的基本使用
3.5.1 信号注册
signal() 函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum是信号的编号,handler是一个函数指针,就是该线程收到该信号时回调的函数。
3.5.2 信号发送
kill()函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid 是信号的接受者的pid,sig是发送的信号的类型。
3.5.3 信号类型
需要注意的是 SIGKILL 和 SIGSTOP无法被捕捉和忽略。
3.5.4 代码实现
代码如下
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>void handler(int signum)
{if(signum == SIGINT){printf("Bye,World!\n");exit(0);}else {printf("error signal\n");}
}int main(int argc, char *argv[]) {printf("start program %s, pid: %d, ppid: %d \n", argv[0], getpid(), getppid());pid_t pid = fork();if (-1 == pid) {printf("fork process failed. errno: %u, error: %s\n", errno, strerror(errno));exit(-1);} if (pid == 0) { // child signal(SIGINT,handler);while(1){printf("I am Child Process, alive!\n");sleep(2);}} else { // parent char c;while(1){printf("To terminate Child Process.Yes(Y) or No(N)?\n");scanf("\n%c",&c);if(c == 'N'){sleep(2);}else if (c == 'Y'){kill(pid,SIGINT);break;}else {printf("wrong inout ,please input Y or N\n");}}} return 0;
}
3.6 在Linux 下模拟哲学家 就餐,提供死锁和非死锁解法
信号量实现的死锁解法
// use semaphore
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<time.h>
#include <errno.h> sem_t chopstick[5] ;
// sem_t room ;int msleep(long msec);
void think(int );
void eat(int );
void * philosppher(void *);// 哲学家逆时针坐着int main ()
{ srand((unsigned)time(NULL)); //sem_init(&room,0,4);for(int i = 0;i < 5;i++){sem_init(chopstick + i,0,1);}pthread_t phi_thread[5] ;int thread_args[5] = {0,1,2,3,4};for (int i = 0; i < 5; i++){pthread_create(phi_thread+i,NULL,philosppher,thread_args+i);}for(int i = 0; i < 5; i++){pthread_join(phi_thread[i],NULL);}// sem_destroy(&room);for(int i = 0;i < 5;i++){sem_destroy(chopstick+i);}return 0;
}void * philosppher (void * arg)
{int i = *(int *)arg;while(1){think(i);// sem_wait(&room); // 一次只允许四位哲学家进入餐厅sem_wait(chopstick + i); // 取左手边的筷子sem_wait(chopstick + ((i+1)%5)); // 取右手边的筷子eat(i);sem_post(chopstick + ((i+1)%5)); // 放下右手边的筷子sem_post(chopstick + i); // 放下左手筷子// sem_post(&room);}
}void think(int i)
{printf("philosopher %d is thinking\n",i);long interval = rand()%401+100;msleep(interval);}void eat(int i)
{printf("philosopher %d is eating\n",i);long interval = rand()%401+100;msleep(interval);
}// sleep thread 'msec' millisecond
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){// EINVAL : Invalid argumenterrno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);// EINTR: Interrupted function call// when Interruption of system calls and library functions return res;
}
信号量实现非死锁解法
// use semaphore no deadlock
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<time.h>
#include <errno.h> #define N 5
#define LEFT (i-1)%N // i的左邻居编号
#define RIGHT (i+1)%N // i的右邻居编号
#define THINKING 0
#define HUNGRY 1
#define EATING 2int state[5]; // 记录每一位哲学家的状态
sem_t s[5] ;
sem_t mutex; // 互斥信号量int msleep(long msec);
void think(int );
void eat(int );
void * philosppher(void *);
void test(int);
void get_chopsticks(int );
void release_chopsticks(int );
// 哲学家逆时针坐着int main ()
{ srand((unsigned)time(NULL)); //sem_init(&room,0,4);for(int i = 0;i < 5;i++){sem_init(s + i,0,0);}sem_init(&mutex,0,1);pthread_t phi_thread[5] ;int thread_args[5] = {0,1,2,3,4};for (int i = 0; i < 5; i++){pthread_create(phi_thread+i,NULL,philosppher,thread_args+i);}for(int i = 0; i < 5; i++){pthread_join(phi_thread[i],NULL);}// sem_destroy(&room);sem_destroy(&mutex);for(int i = 0;i < 5;i++){sem_destroy(s+i);}return 0;
}void * philosppher (void * arg)
{int i = *(int *)arg;while(1){think(i);get_chopsticks(i);eat(i);release_chopsticks(i);}
}
void get_chopsticks(int i)
{sem_wait(&mutex); // 进入临界区state[i] = HUNGRY; // 设置哲学家状态为饥饿test(i); // 尝试获得两把筷子sem_post(&mutex); // 出临界区 sem_wait(s+i); // 如果得不到需要的筷子就阻塞
}void release_chopsticks(int i)
{sem_wait(&mutex); state[i] = THINKING;test(LEFT);test(RIGHT);sem_post(&mutex);
}void think(int i)
{printf("philosopher %d is thinking\n",i);long interval = rand()%401+100;msleep(interval);}void eat(int i)
{printf("philosopher %d is eating\n",i);long interval = rand()%401+100;msleep(interval);
}// sleep thread 'msec' millisecond
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){// EINVAL : Invalid argumenterrno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);// EINTR: Interrupted function call// when Interruption of system calls and library functions return res;
}
void test(int i) { if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) { state[i] = EATING ; sem_post(s+i) ; }
}
条件变量+互斥锁
// use conditonal variable and mutex
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<time.h>
#include <errno.h>
#include <stdbool.h>pthread_cond_t ChopstickReady[5]; // 用于同步的条件变量
bool chopstick[5] = {1,1,1,1,1}; // 每个筷子的可用状态
pthread_mutex_t lock;int msleep(long msec);
void think(int );
void eat(int );
void * philosppher(void *);
void get_chopsticks(int pid);
void release_chopsticks(int pid);// 哲学家逆时针坐着int main ()
{ srand((unsigned)time(NULL)); // 初始化互斥锁和条件变量pthread_mutex_init(&lock,0);for (int i=0;i<5;i++){pthread_cond_init(ChopstickReady+i,0);}pthread_t phi_thread[5] ;int thread_args[5] = {0,1,2,3,4};for (int i = 0; i < 5; i++){pthread_create(phi_thread+i,NULL,philosppher,thread_args+i);}for(int i = 0; i < 5; i++){pthread_join(phi_thread[i],NULL);}pthread_mutex_destroy(&lock);for (int i=0;i<5;i++){pthread_cond_destroy(ChopstickReady+i);}return 0;
}void * philosppher (void * arg)
{int i = *(int *)arg;while(1){think(i); // 思考get_chopsticks(i); // 拿筷子eat(i); // 吃饭release_chopsticks(i); // 放下筷子}
}void think(int i)
{printf("philosopher %d is thinking\n",i);long interval = rand()%401+100;msleep(interval);}void eat(int i)
{printf("philosopher %d is eating\n",i);long interval = rand()%401+100;msleep(interval);
}// sleep thread 'msec' millisecond
int msleep(long msec)
{struct timespec ts;int res;if (msec < 0){// EINVAL : Invalid argumenterrno = EINVAL;return -1;}ts.tv_sec = msec / 1000;ts.tv_nsec = (msec % 1000) * 1000000;do {res = nanosleep(&ts, &ts);} while (res && errno == EINTR);// EINTR: Interrupted function call// when Interruption of system calls and library functions return res;
}void get_chopsticks(int pid)
{int left = pid;int right = (pid + 1) % 5;pthread_mutex_lock(&lock);// 获取左手筷子while (!chopstick[left]){pthread_cond_wait(ChopstickReady + left,&lock);}chopstick[left] = false;// 获取右手筷子while (!chopstick[right]){pthread_cond_wait(ChopstickReady + right,&lock);}chopstick[right] = false;pthread_mutex_unlock(&lock);}
void release_chopsticks(int pid)
{int left = pid;int right = (pid +1) % 5;pthread_mutex_lock(&lock);// 释放左手筷子// 当没有人在等待左手的筷子if(!(pthread_cond_signal(ChopstickReady + left))){chopstick[left] = true;}// 当没有人在等待右手的筷子if(!(pthread_cond_signal(ChopstickReady + right))){chopstick[right] = true;}pthread_mutex_unlock(&lock);}
参考资料:
- 操原上机作业(二)
- fork(2)
- errno(3)
- linux子进程知道父进程退出的解决方案
- Solved – Undefined reference to ‘pthread_create’ in Linux with Explanation
- Is there an alternative sleep function in C to milliseconds?
- Linux环境下编程(一)——进程fork()的使用
- pthread_create(3)
- pthread_join(3)
- Linux Tutorial:POSIX Threads
- Linux信号(signal)
- 哲学家就餐问题
OS实验:进程管理与死锁相关推荐
- OS X进程管理之launchctl
OS X进程管理之launchctl Apple官方文档 如果 Mac 无法完成启动,请尝试安全模式 如果 Mac 无法开机应如何处理 在 Mac OS X 中设置固件密码保护 如何重置 Mac 上的 ...
- OS之进程管理 --- 死锁
什么是死锁 在正常操作模式下,进程按如下顺序来使用资源: 申请:进程请求资源 使用:进程对资源进行操作 释放:进程释放资源 当一组进程中的每一个进程度在等待一个事件,而这事件只能有一组进程的另一个进程 ...
- [OS复习]进程管理2
问题:多个进程竞争内存资源 1.解决方法 方案一:采用交换技术,换出一部分进程到外存,以腾出内存空间 方案二:采用虚拟存储技术,每个进程只能装入一部分程序和数据(存储管理部分) 2.对换技术(交换技术 ...
- python中的os abort_Python::OS 模块 -- 进程管理
这里我们介绍os模块中的进程管理相关的操作. os模块提供给了我们访问操作系统功能的接口,我们可以通过os模块提供给我们的进程管理接口,编写多进程程序,这对编写高效.并发的程序提供了方便. 下面是一个 ...
- python os模块进程管理
2019独角兽企业重金招聘Python工程师标准>>> 有两种方式来实现并发性,一种方式是让每个"任务"或"进程"在单独的内在空间中工作,每个 ...
- [OS复习]进程管理5
线程 1.多线程 操作系统中引入进程的目的: 为了描述和实现多个程序的并发执行,以改善资源利用率及提高系统的吞吐量. 操作系统引入线程的目的: 这是为了减少程序并发执行时系统所付出的额外开销(减少管理 ...
- [OS复习]进程管理4
进程调度算法(Short-Term) 1.先来先服务(FCFS) 该方法按照进程到达的先后顺序排队,每次调度队首的进程(就像超市中购物付款一样). FCFS算法属于非剥夺调度方式,实现简单,看似公平. ...
- Python之OS模块进程管理介绍--os.fork()
转自:http://davidbj.blog.51cto.com/4159484/1240586 有两种方式来实现并发性,一种方式是让每个"任务"或"进程"在单 ...
- 计算机实验进程管理与虚拟机,虚拟机VMware进程控制实验.docx
虚拟机VMware进程控制实验 实验6:进程控制操作 1.实验目的 1.了解进程的概念: 2.熟悉Linux的前台与后台进程控制操作: 3.掌握利用进程监控工具来维护系统的正常运行: 2.实验内容 1 ...
最新文章
- Magento开发的特点有哪些?
- 梯度下降(Gradient Descent),一句代码,一个式子
- 回调函数案列(C高级)
- flstudio插件找不到_Eclipse4.17安装spring插件的问题
- 【kafka】kafka 错误代码解释
- AI 如何应用于油气勘探?
- 精通python工资高吗-软件测试,如何工资过万?
- burpsuite_pro的使用
- 电脑ps计算机磨皮,ps磨皮教程
- pm2.5计算和单位换算
- 2021年P气瓶充装新版试题及P气瓶充装证考试
- 给计算机图片文件夹加密码,文件夹怎么设置密码
- pl sql迁移oracle,Oracle数据库安装及使用PLSQL数据迁移
- python----引用其他py文件中的函数
- Telerik Silverlight 之Charting控件的使用
- ios pushViewController 页面不跳转问题解决
- android自定义手势,Android编程实现自定义手势的方法详解
- solidworks批量图号分离_SolidWorks如何利用宏来快速的实现 “图号名称”分离 呢?...
- matlab 回归分析箱,RegrToolbox5 matlab回归分析工具箱 - 下载 - 搜珍网
- 计算机本地连接xp,xp本地连接不见了怎么办【图解】