【要求】所有练习题保留题目要求,在题目要求后面作答:
代码要求有注释,代码中适当标注关键代码为红色。
要有运行结果的截图。
每题最后应该有对程序的适当分析和总结!
注意格式排版,内容分析注意列条目,展开清楚地阐述。
1、分析理解多个进程的创建1)若一个程序中有这样的代码,则有几个进程,父子关系如何?
#include
#include
#include
#include
int main(void){
pid_t pid,pid2;
char *message;
int x;
pid = fork();
pid2 = fork();
if (pid < 0)
{ perror("fork failed");
exit(1); }
补充完整后面的代码,使多个进程输出各自身份,并符合正确的亲缘关系
if ( )
{ message = "This is the child\n";
x = 0; }
……
printf("%s I'm %d, x=%d,my father is:%d\n",message,x,getpid(),getppid());
return 0;
}//main
给出代码及执行效果抓图,并要有说明分析!!!!谁先谁后,为什么。
代码如下:
#include
#include
#include
#include
int main(void)
{
pid_t pid,pid2;
char *message;
int x;
pid = fork();
pid2 = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid>0 && pid2>0)
{//父进程
message = "This is the father\n";
x = 1;
}else if(pid>0 && pid2==0)
{//子进程2
message = "This is the second child\n";
x = 2;
}else if(pid==0 && pid2>0)
{//子进程1
message = "This is the first child\n";
x = 3;
}else if(pid==0 && pid2==0)
{//孙进程
message = "This is the grandson\n";
x = 4;
}
printf("%s I'm %d, x=%d,my father is:%d\n",message,x,getpid(),getppid());
return 0;
}
执行效果如下:
当前父进程(7724)的父进程先运行(4258),在父进程(7724)的运行过程中产生了子进程,pid=0,pid2>0产生子进程1(7725);pid>0,pid2=0产生子进程2(7726),而pid=0,pid2=0使得子进程1(7725)又产生了新的子进程(7727);但由于父进程pid=0产生子进程1(7725)同时运行pid2,子进程2和孙进程(7727)顺序不能确定
2)若有如下的代码
for(i = 0; i <4; i++)
{
pid = fork();
……
请分析将产生多少个进程?要有说明分析!!!!
代码如下:
#include
#include
#include
#include
int main(void)
{
pid_t pid;
for(int i = 0; i < 4; i++)
{
pid = fork();
}
printf("%d\n",getpid());
return 0;
}
效果如下:
产生16个进程
调用一次fork()函数将得到两个返回值,而两个进程继续执行下一条fork(),则返回4个进程ID的值,依次可得到执行第i个fork函数后有2的i次方个进程
2、解释执行效果
若有下面的功能代码,执行会有怎样的输出?不只有抓图,要有说明分析!!!!谁先谁后,哪里停顿过。
int main(void){
pid_t a;
a = fork();
if (a == 0) {
sleep(2);
execlp ("ps" ,"ps",NULL);
printf("%s I'm %d, x=%d,my father is:%d\n",message,getpid(),getppid());
}
else
printf("%s I'm %d, x=%d,my father is:%d\n",message,getpid(),getppid());
return 0;
}
代码如下:
执行如下图:
执行父进程(6393)时,语句直接执行;当a=0执行其子进程时先执行sleep休眠两秒,然后执行execlp运行ps展示当前进程,覆盖子进程的内容,因此不再执行printf语句。
3. 体验进程/线程的顺序控制。(基于对例5-9,5-11的理解,注意不是用sleep控制)
1)编写一个可产生父子进程的程序,使执行时子进程先printf输出内容,然后父进程再printf输出。
代码如下:
#include
#include
#include
#include
#include
int main(void)
{
pid_t pc1, pw1;
pc1 = fork();
if(pc1 < 0){
perror("fork failed");
exit(1);
}
if(pc1 > 0){ //父进程
pw1 = wait(NULL); //进入阻塞状态,等待其有亲缘的进程死亡后重新唤醒
printf("Catch a dead child process with pid:%d\n",pw1);
printf("I'm %d, The father process\n",getpid());
}else{
printf("I'm %d, The child process,my father process is %d\n",getpid(),getppid());
}
return 0;
}
效果如下:
pc1父进程运行时,当pw1 = wait(NULL)时,系统调用会使调用它的进程等待,进入阻塞状态,等待其有亲缘的子进程死亡进入僵死状态时,系统会重新把它从阻塞态唤醒,因此当父进程阻塞,子进程运行printf语句,运行完毕后子进程死亡,父进程被唤醒,执行printf语句
2)编写一个可产生两个线程(一个输出AAAAAA,一个输出BBBBBB)的程序,代码中要求控制线程的输出为AAAAAABBBBBB的顺序。然后修改控制代码,重新编译执行,得到另一种输出顺序,得到BBBBBBAAAAAA的输出。
代码如下:
#include
#include
#include
#include
#include
char message[50] = "Hello World";
void *thread_function2(void *arg)
{
printf("AAAAAA");
pthread_exit("子线程结束");
}
void *thread_function1(void *arg)
{
printf("BBBBBB");
pthread_exit("子线程结束");
}
int main(void)
{
int res1, res2;
pthread_t thread1, thread2;
void *thread_result1, *thread_result2;
res1 = pthread_create(&thread1,NULL,thread_function1,(void*)message);
res2 = pthread_create(&thread2,NULL,thread_function2,(void*)message);
if(res1 != 0 || res2 != 0)
{
perror("Thread creation failed!");
exit(EXIT_FAILURE);
}
res1 = pthread_join(thread1,&thread_result1);
res2 = pthread_join(thread2,&thread_result2);
if(res1 != 0 || res2 != 0)
{
perror("Thread join failed!\n");
exit(EXIT_FAILURE);
}
printf("\n");
exit(EXIT_FAILURE);
}
编译执行 效果如下:
修改代码:修改代码只需调换void() 中thread_function1和2的顺序即可
重新编译执行效果如下:
由主进程产生一个线程,线程的功能为修改进程变量message字符串里的内容,利用join使主线程阻塞等待子线程结束返回后才继续执行,从而控制了两者的执行顺序。
4. 体验线程对共享变量的处理应互斥,否则会出现线程不安全问题。(选做)
两个线程对共享的进程变量做加1 *** 作,结果加和是错的(例5-12),多次运行测试效果,体会程序执行原理,给出认识总结。
#include
#include
#include
#include
#include
long x = 0, y = 0, z = 0;
void * thread_function1(void *arg)
{
printf("===thread_function1 is running. \n");
while(1)
{
x++;
z++; }//while
pthread_exit("===Thank you for your CPU time!");
}
void * thread_function2(void *arg)
{
printf("===thread_function2 is running. \n");
while(1)
{
y++;
z++; }//while
pthread_exit("===Thank you for your CPU time!");
}
int main(){
int res;
pthread_t thread1;
pthread_t thread2;
void *thread_result;
res = pthread_create(&thread1,NULL,thread_function1,NULL);
res = pthread_create(&thread2,NULL,thread_function2,NULL);
sleep(1);
printf("x = %d\ny = %d\nx+y = %d\nz = %d\n",x,y,x+y,z);
exit(EXIT_FAILURE);
}
代码功能很简单,主进程定义了三个变量x、y、z,主进程运行产生的两个线程共享这些变量 ,thread1和thread2分别持续对x和y做加1 *** 作,但同时两者每次做加法,也对z做累加1的 *** 作。主线程睡眠1秒后打印输出结果,然后进程结束。
执行效果如下:
多次运行测试:
两线程获得的CPU时间不确定,所以加法做的次数不同,x不等于y没什么问题;但在每次x加1、y加1时,它们也都使z加1了,正常逻辑下z的值和x+y的值应是相等的。
问题实际上出在双方对z的共同 *** 作上。对z的加法 *** 作在系统底层涉及从内存取到寄存器、再在寄存器累加、再保存回变量的内存的过程。两个线程都对z变量的内容做加法,由于线程并发执行,它们的加法 *** 作相互穿插,难免出现其中一个的加法是在另一个加到一半的基础上做的,在错误的基础上做工作很容易出现错误的可能。
解决的办法是,需要提供控制手段,在多方对某种共享资源具有竞争关系时,保证只有一个使用者用完资源后另一个才能使用,这就是典型的资源互斥处理问题。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)