实验一:处理机调度
- 在root下建立一个文件夹名字叫CProgram,进入CProgram文件夹。
建立文件夹,使用命令: root@kali:~# mkdir CProgram 如果要删除,使用命令: root@kali:~# rm -rf 文件夹名 进入该文件夹,使用命令: root@kali:~# cd CProgram |
rm 命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉。使用一定要谨慎。
- 试试新建一个C程序,并实现helloword功能。
使用命令:新建文件、编译文件、运行文件
root@kali:~/CProgram# gedit helloworld.c root@kali:~/CProgram# gcc helloworld.c -o helloworld 生成可执行文件helloworld。 root@kali:~/CProgram# ./helloworld |
3.fork函数的使用
fork()是创建进程函数。
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
3.1 编写程序forkPID.c
程序代码:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t pid; printf("Start of fork testing\n"); pid=fork(); if(pid>0) printf("In parent\n"); else printf("In child\n"); printf("Return of fork sucess: pid:%d\n",pid); return 0; } |
输出效果:
Start of fork testing In parent Return of fork sucess: pid:2575 In child Return of fork sucess: pid:0 |
fork函数被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
使用命令:新建文件、编译文件、运行文件
root@kali:~/CProgram# gedit forkPID.c root@kali:~/CProgram# gcc forkPID.c -o forkPID root@kali:~/CProgram# ./forkPID |
3.2编写程序forkMoreTest.c
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t pid; int i; printf("Start of fork testing\n"); pid=fork(); if(pid>0) { printf("In parent\n"); i=5; } else { printf("In child\n"); i=10; } printf("Return of fork sucess: pid:%d\n",pid); printf("The value of i is :%d\n",i); return 0; } |
输出效果:
Start of fork testing In parent Return of fork sucess: pid:4922 The value of i is :5 In child Return of fork sucess: pid:0 The value of i is :10 | Start of fork testing In parent In child Return of fork sucess: pid:0 The value of i is :10 Return of fork sucess: pid:4942 The value of i is :5 | Start of fork testing In parent Return of fork sucess: pid:4945 In child The value of i is :5 Return of fork sucess: pid:0 The value of i is :10 |
提问:
比较三次运行效果的异同,试回答为什么同样的代码会有产生不同的运行效果。
- 创建线程
线程可以在进程执行期间的任意时刻被创建,且线程的数量没有必要事先指定,这样的线程称为动态线程。
线程可以用pthread_create动态创建。函数pthread_create能创建进程,并将它放入就绪队列。
pthread_create包括四个参数:线程ID、线程属性、线程运行的函数、函数的参数。示例代码如下是:
#include <stddef.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> void print_message(char* ptr); int main() { pthread_t thread1,thread2; char *msg1="Hello\n"; char *msg2="World\n"; pthread_create(&thread1,NULL,(void *)(&print_message),(void *)msg1); pthread_create(&thread2,NULL,(void *)(&print_message),(void *)msg2); sleep(1); return 0; } void print_message(char* ptr ) { int retval; printf("Thread ID %lx\n",pthread_self()); printf("%s\n",ptr); pthread_exit(&retval); return; } 输出效果: Thread ID 7fa265a77700 Hello Thread ID 7fa265276700 World |
使用命令:新建文件、编译文件、运行文件
root@kali:~/CProgram# gedit threadID.c root@kali:~/CProgram#gcc -pthread threadID.c -o threadID root@kali:~/CProgram# ./threadID |
提问:
代码sleep(1)的作用是什么?不用会怎么样?
- 挂起线程
函数pthread_join用于挂起当前线程直至指定线程终止。函数原型:
int thread_join(pthread_t th, void **thread_return)
参数th是一个线程标识符,用于指定等待其终止的线程。参数thread_return用于存放其他线程的返回值。如果执行成功,那么参数th的返回值将保存在有参数thread_return指向的地址中,函数返回0,否则返回一个非零值。
代码中如果没有thread_join;主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入thread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
示例代码如下:
#include <stddef.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> void print_msg(char *ptr); int main() { pthread_t thread1,thread2; int i,j; void *retval; char *msg1="Hello\n"; char *msg2="World\n"; pthread_create(&thread1,NULL,(void*)(&print_msg),(void*)msg1); pthread_create(&thread2,NULL,(void*)(&print_msg),(void*)msg2); pthread_join(thread1,&retval); pthread_join(thread2,&retval); return 0; } void print_msg(char *ptr) { int i; for(i=0;i<10;i++) printf("%s",ptr); return; } |
试一试:
删除代码1、2,或者删除1和2的区别是什么? (1)pthread_create(&thread1,NULL,(void*)(&print_msg),(void*)msg1); (2)pthread_create(&thread2,NULL,(void*)(&print_msg),(void*)msg2); |
使用命令:
root@kali:~/CProgram# gcc pthread_join.c -o pthread_join -lpthread root@kali:~/CProgram# ./pthread_join |
输出效果:
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World |
- 线程同步-1,单个缓冲区,生产者消费者/读写问题
POSIX提供了两种类型的同步机制,互斥锁mutex和条件变量(condition variable).
互斥锁是一个简单的锁定命令,它可以用来锁定对临界资源的访问。
示例代码:
#include <stddef.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> #define FALSE 0 #define TRUE 1 void readfun(); void writefun(); char buffer[256]={0}; int buffer_has_item=0; int retflag=FALSE; pthread_mutex_t mutex;//定义了一个互斥锁 int main() { pthread_t reader; pthread_mutex_init(&mutex,NULL);//初始化互斥锁 pthread_create(&reader,NULL,(void*)&readfun,NULL);//创建读者线程 writefun();//写者在主进程中 } void readfun() { while(1) { if(retflag) return; pthread_mutex_lock(&mutex);//锁定临界区 if(buffer_has_item==1) { printf("I'm reading:%s",buffer); buffer_has_item=0; } pthread_mutex_unlock(&mutex);//释放临界区 } return; } void writefun() { int i=0; while(1) { if(i==10) { retflag=TRUE; return; } pthread_mutex_lock(&mutex);//锁定临界区 if(buffer_has_item==0) { printf("I'm writing\n"); sprintf(buffer,"This is %d\n",i++); buffer_has_item=1; } pthread_mutex_unlock(&mutex);//释放临界区 } return; } |
使用命令:
root@kali:~/CProgram# gedit mutex.c root@kali:~/CProgram# gcc mutex.c -o mutex -lpthread root@kali:~/CProgram# ./mutex |
- 线程同步-2,多个缓冲区,生产者消费者/读写问题
在程序中使用互斥锁虽然可以解决一些资源竞争的问题,但是互斥锁只有两种状态,这使得它的用处非常有限。
条件变量是对互斥锁的补充,它允许线程阻塞并等待另一个线程发送的信号。当收到信号时,阻塞的线程被唤醒并试图锁定预支相关的互斥锁。
示例代码:
#include <stdio.h> #include <pthread.h> #define BUFFERA_SIZE 4 #define OVER (-1) struct producers { int buffer[BUFFERA_SIZE]; pthread_mutex_t lock; int readpos,writepos; pthread_cond_t notempty; pthread_cond_t notfull; }; void init(struct producers *b) { pthread_mutex_init(&b->lock,NULL); pthread_cond_init(&b->notempty,NULL); pthread_cond_init(&b->notfull,NULL); b->readpos=0; b->writepos=0; } void put(struct producers *b, int data) { pthread_mutex_lock(&b->lock); while((b->writepos+1)%BUFFERA_SIZE==b->readpos) pthread_cond_wait(&b->notfull,&b->lock); b->buffer[b->writepos]=data; b->writepos++; if(b->writepos>=BUFFERA_SIZE) b->writepos=0; pthread_cond_signal(&b->notempty); pthread_mutex_unlock(&b->lock); return; } int get(struct producers *b) { int data; pthread_mutex_lock(&b->lock); while(b->writepos==b->readpos) pthread_cond_wait(&b->notempty,&b->lock); data=b->buffer[b->readpos]; b->readpos++; if(b->readpos>=BUFFERA_SIZE) b->readpos=0; pthread_cond_signal(&b->notfull); pthread_mutex_unlock(&b->lock); return data; } struct producers buffer; void *producer(void *data) { int n; for(n=0;n<10;n++) { printf("Producer:%d-->\n",n); put(&buffer,n); } put(&buffer,OVER); return NULL; } void *consumer(void *data) { int d; while(1) { d=get(&buffer); if(d==OVER) break; printf("Consumer:-->%d\n",d); } return NULL; } int main() { pthread_t tha,thb; void *retval; init(&buffer); pthread_create(&tha,NULL,producer,0); pthread_create(&thb,NULL,consumer,0); pthread_join(tha,&retval); pthread_join(thb,&retval); return 0; } |