Proccess And Thread
1、fork()和exec()的区别
fork()
创建新的进程,父子进程拥有相同的内存映像;
对于父进程来说fork获得的pid是创建的子进程的pid,对于子进程来说fork获得的pid是0;
#include <unistd.h>
#include <stdio.h>
int main(){
pid_t pid;
pid = fork();
if( pid == 0 ){
while(1){
sleep(1);
printf("haha\n"); //子进程会一直打印haha
}
}
if( pid > 0 ){
while(1){
sleep(1);
printf("hehe\n"); //父进程会一直打印hehe
}
}
return 0;
}
exec()
是以新的程序去代替原来的程序,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。fork()
后产生父子进程,拥有相同内存印象。子进程执行exec,修改内存映像,运行新的程序.
#include <unistd.h>
#include <stdio.h>
int main(){
pid_t pid;
pid = fork();
if(pid==0){
execl("/bin/ls", "ls", "-l", 0); //子进程切换,调用执行新的程序,不再会打印出haha
while(1){
sleep(1);
printf("haha\n");
}
}
if(pid>0){
while(1){
sleep(1);
printf("hehe\n"); //父进程继续打印hehe
}
}
}
2、pthread
#include <stdio.h>
#include <pthread.h>
int a;
void * th(void *p){
int i = 0;
while(1){
a = 1; i++;
sleep(1);
if(i<5)
printf("haha\n");
}
}
int main()
{
int i = 0;
a = 0;
pthread_t myth;
pthread_create(&myth, NULL, th, NULL);
while(1)
{
i++; sleep(1);
if(i <= 5)
printf("a=%d hehe\n", a);
}
return 0;
}
pthread_create(&myth, NULL, th, NULL)
生成了线程:表示要执行的程序标段。
3、进程和线程的区别
同一个进程的线程之间是共享:程序段,数据段,代码段的
但是:PSW,PC,堆栈,寄存器是不共享的。
4、Mutual Exclusion with Busy waiting
(1) 锁变量
连续测试一个变量直到出现某个值为止,称为忙等待。
用于忙等待的锁称为自旋锁。
while(TRUE){
while(turn!=0); /*忙等待*/
critical_region();
turn = 1;
noncritical_region();
}
while(TRUE){
while(turn!=1);/*忙等待*/
critical_region();
trun = 0;
noncritical_region(;)
}
需要严格地轮换执行
(2) Peterson解法
用了两个变量避免轮转:
#define FALSE 0
#define TRUE 1
#define N 2
int turn;
int interested[N];
void enter_region(int process)
{
int other;
other = 1 - process;
interested[process] = TRUE;
turn = process;
while(turn == process && interested[other] == TRUE); //空语句,无法进入关键区
}
void leave_region(int process){
interested[process] = FALSE; //表示离开临界区
}
(3) TSL指令
TSL RX, LOCK
:测试并加锁;将锁字lock读到寄存器RX中,然后将锁值设为1;
读字和写操作保证是不可分的。即该指令结束前不允许其他处理器访问该内存字。执行TSL指令需要锁住总线
注意,在TSL指令中,LOCK为0是表示未上锁,为1时表示上锁
enter_region:
TSL REGISTER, LOCK |复制锁值到寄存器,并将锁设置为1
CMP REGISTER, #0 |锁是0吗?是0表示没有上锁,可以进入关键区
JNE enter_region |锁值非零,已经上锁,不能进入关键区
RET |返回调用者,进入临界区
leave_region:
MOVE LOCK, #0 |将锁值设置为0,解锁
RET |离开关键区
另外TSL指令可以被XCHG指令代替:
enter_region:
MOVE REGISTER, #1
XCHG REGISTER, LOCK
CMP REGISTER, #0
JNE enter_region
RET
leave_region:
MOVE LOCK, #0
RET
5、Sleeping and Wakeup
具有严重的竞争条件的生产者与消费者问题。
丢失唤醒信号!!!会导致整个的睡眠
6、信号量
#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void produce()
{
int item;
while(TRUE){
item = produce_item();
down(&empty);
down(&mutex);
isert_item(item);
up(&mutex);
up(&full);
}
}
void consume()
{
int item;
while(TRUE){
down(&full);
down(&mutex);
item = remove_item();
up(&mutex);
up(&empty);
consume_item(item);
}
}
7、互斥量
信号量的简化版本。仅适用于管理共享资源或一小段代码。在实现用户空间线程包时非常有用。值只有0和1。这是不同于semaphore的显著区别。
mutex_lock:
TSL REGISTER, LOCK
CMP RESISTER, #0
JZE ok |锁是0吗?是零的话,那么跳转
CALL thread_yield |与忙等待不同之处,让出了CPU的使用权
JMP mutex_lock
ok: RET
mutex_unlock:
MOVE mutex, #0
RET
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LX7C5J9V-1625280764315)(C:/Users/%E5%AD%99%E8%95%B4%E7%90%A6/AppData/Roaming/Typora/typora-user-images/image-20210621184404415.png)]
互斥量和conditional variable联合使用,解决生产者和消费者问题:
#include<stdio.h>
#include<pthread.h>
#define MAX 1000000000 /*需要生产的数量*/
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer = 0;
void *producer(void *ptr)
{
int i;
for(i = 1; i<= MAX;i++){
pthread_mutex_lock(&the_mutex); //给mutex上锁
while(buffer! = 0)pthread_cond_wait(&condp, &the_mutex);
buffer = i;
pthread_cond_signal(&condc); //唤醒消费者
pthread_mutex_unlock(&the_mutex); //释放缓冲区
}
pthread_exit(0);
}
void consumer(void *ptr)
{
int i;
for(i = 0; i<=MAX; i++){
pthread_mutex_lock(&the_mutex);
while(buffer == 0)pthread_cond_wait(&condc, &the_mutex);
buffer = 0;
pthread_cond_signal(&condp); //唤醒生产者
pthread_mutex_unlock(&the_mutex);
}
pthread_exit(0);
}
int main()
{
pthread_t pro, con;
pthread_mutex_init (&the_mutex, 0);
pthread_cond_init(&condc, 0);
pthread_cond_init(&condp, 0);
pthread_create(&con, 0, consumer, 0);
pthread_create(&pro, 0, producer, 0);
pthread_join(pro, 0);
pthread_join(con, 0);
pthread_cond_destory(&condc);
pthread_cond_destory(&condp);
pthread_mutex_destory(&the_mutex);
}
8、经典IPC问题
(1)哲学家就餐问题
#define N 5
#define LEFT (i+N-1)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int semaphore;
int state[N];
semaphore mutex = 1; //对状态表的互斥访问
semaphore s[N]; //到底有无获得叉子
void philosopher(int i)
{
while(TRUE){
}
}
void take_forks(int i)
{
down(&mutex); //保护状态表
state[i] = HUNGRY;
test(i); //询问一下邻居能不能吃
up(&mutex);
down(&s[i]); //拿不到叉子会阻塞
}
void put_forks(i)
{
down(&mutex);
state[i] = THINKING;
test(LEFT);
test(RIGHT);
up(&mutex);
}
void test(i)
{
if(state[i] == HUNGERY && state[LEFT] != EATING && state[RIGHT] != EATING){
state[i] = EATING;
up(&s[i]);
}
}
(2)读者写者问题
typedef int semaphore;
semaphore mutex = 1; //互斥:rc
semaphore db = 1; //互斥:数据库访问
int rc = 0; //读者数
void reader()
{
while(TRUE){
down(&mutex);
rc = rc+1;
if(rc==1)down(&db);
up(&mutex);
read_data_base;
down(&mutex);
rc = rc -1;
if(rc == 0) up(&db);
up(&mutex);
use_data_read;
}
}
void writer()
{
while(TRUE){
think_up_data();
down(&db);
write_data_base();
up(&db);
}
}
9、消息传递解决生产者和消费者问题
#define N 100
void producer(void)
{
int item;
message m;
while(TRUE){
item = produce_item();
receive(consumer, &m);
build_message(&m, item);
send(consumer, &m);
}
}
void consumer(void)
{
int item, i;
message m;
for(int i = 0;i<N;i++){
send(producer, &m);
}
while(TRUE){
}
}
#define N 100
void producer(void)
{
int item;
message m;
while(TRUE){
item = produce_item();
receive(consumer, &m);
build_message(&m, item);
send(consumer, &m);
}
}
void consumer(void)
{
int item, i;
message m;
for(int i = 0;i<N;i++){
send(producer, &m);
}
while(TRUE){
}
}