实验环境

  • Ubuntu 18.04
  • Visual Code

实验记录

实验一:观察进程标识符

所用代码如下:

#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 
int main() 
{ 
//pid_t是数据类型,实际上是一个整型,通过typedef重新定义了一个名字,用于存储进程id 
pid_t pid;
//scanf("%d",&i);//让进程等待,方便使用shell观察进程pid
pid= getpid();   //getpid()函数返回当前进程的id号 
printf("Process id :%d\n", pid); 
return 0; 
} 

使用如下命令打开gedit

gedit your_file_name

将代码复制到打开的文本编辑器里,并保存。使用如下指令进行编译

gcc your_file_name -o output_name

需要注意的是源代码必须以.c结尾,否则会报如下错误
在这里插入图片描述运行,发现了进程的pid为:
在这里插入图片描述将代码的scanf注释取消,重新运行,使用如下指令

ps -al

可在进程列表中观察到对于的进程:
在这里插入图片描述

实验二:子进程的创建

	#include <stdio.h> 
	#include <sys/types.h> 
	#include <unistd.h> 
	int main() 
	{ 
	pid_t cid; 
	printf("Before fork Process id :%d\n", getpid()); 
	/* 
	fork()函数用于创建一个新的进程,该进程为当前进程的子进程,创建的方法是:将当前进程的内存内容完整拷贝一份到内存的另一个区域,两个进程为父子关系,他们会同时(并发)执行fork()语句后面的所有语句。 
	fork()的返回值: 
	如果成功创建子进程,对于父子进程fork会返回不同的值,对于父进程它的返回值是子进程的进程id值,对于子进程它的返回值是0. 
	如果创建失败,返回值为-1. 
	*/ 
	cid = fork(); 
	printf("After fork, Process id :%d\n", getpid()); 
	int i;
	//scanf("%d",&i);//阻塞函数
	return 0; 
	} 

运行结果为
在这里插入图片描述
可以发现两个现象:

  • 出现了两个Afterfock语句,原因在于使用了fock函数后,Linux创建了一个进程,该进程的EIP寄存器值被设为fock()函数后的语句
  • 在两个Afterfock语句之间出现了shell提示符,该原因为在子进程在主进程运行完成之后才被调度,此时shell进程已被调度,输出了shell提示符。将阻塞函数加入后,再次运行。
    在这里插入图片描述可以发现猜测是正确的。

利用ps -al指令可以观察到PID(process id)和PPID(parent process id)
在这里插入图片描述

实验三:观察进程并发执行

实验代码为:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
    pid_t cid;
    printf("Before fork process id :%d\n", getpid());
    cid = fork();
  if(cid == 0){ //该分支是子进程执行的代码
     printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
       for(int i=0; i<3 ; i++)
            printf("hello\n");
}else{ //该分支是父进程执行的代码
       printf("Parent process id :%d\n", getpid());
       for(int i=0; i<3 ; i++)
            printf("world\n");
    }
   return 0;
}

循环三次的运行结果为:
在这里插入图片描述
循环三百次的结果为:
在这里插入图片描述可以看到…效果似乎不是很理想,三百次的循环也没有将一次进程调度的时间片耗尽,为此最好在循环中加一个阻塞函数sleep即可实现想要观察到的并发效果:
在这里插入图片描述
可以看到,在加了阻塞函数之后,进程主动放弃运行权,后再次被调度到,此时两个进程可以看出并发执行。

实验四:进程内存空间相互独立

实验代码为:

#include <sys/types.h>
#include <unistd.h>
int main()
{
    pid_t cid;
    int x = 100;
    cid = fork();
    if (cid == 0)
    { //该分支是子进程执行的代码
        x++;
        printf("In child: x=%d\n", x);
    }
    else
    { //该分支是父进程执行的代码
        x--;
        printf("In parent: x=%d\n", x);
    }
    return 0;
}

编译运行,可以看到结果如下:
在这里插入图片描述
这验证了两个进程中的变量X是相互独立的。
添加了阻塞函数后的代码为:

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h> 
#include<stdio.h>
int main()
{
    pid_t cid;
    int x = 100;
    cid = fork();
    if (cid == 0)
    { //该分支是子进程执行的代码
        x++;
        printf("In child: x=%d\n", x);
        sleep(3); 
    }
    else
    { //该分支是父进程执行的代码
        wait(NULL); 
        x--;
        printf("In parent: x=%d\n", x);
    }
    return 0;
}

运行结果为:
在这里插入图片描述
可以看到,在加了阻塞函数之后,两个进程的调度被控制了,此时能够更加清晰的观察到两个变量是独立的。

实验五:线程的创建及其观察

实验代码为:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void *threadFunc(void *arg)
{ //线程函数
    printf("In NEW thread\n");
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, threadFunc, NULL); //线程创建函数
    pthread_join(tid, NULL);                      //等待指定的线程结束
    printf("In main thread\n");
    return 0;
}

其中pthread_create的函数声明为:

int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,\
void *(*start_rtn)(void*),void *arg);

可以看到,第三个参数定义了线程的开始地址,第四个参数向线程压入了一个参数。
而pthread_join的函数声明为:

int pthread_join(pthread_t thread, void **retval);

其中第二个参数为线程返回的函数,这个函数的作用是等待指定的进程结束。那为什么要调用这个函数呢?这里有一篇博客解释了这个原因,也简要解释了Linux环境下线程的创建和工作过程。
https://blog.csdn.net/heybeaman/article/details/90896663
采用如下指令编译

gcc helloThread.c -o helloThread -pthread

运行
在这里插入图片描述可以发现,两个线程都被运行了起来。
将pthread_join进行注释,重新编译运行
在这里插入图片描述可以看到,新创建的线程并没有得到运行的机会。
原因可以在上面的博客中找到答案:main主线程创建完新的线程之后就运行完了,此时整个进程已经运行完毕,资源被系统回收,新创建的线程再也没有机会得到运行。

实验六:多个进程的并发运行

实验代码如下:

#include <sys/types.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <pthread.h> 
void* hello(void* arg){ //线程函数 
for(int i=0;i<300;i++)
printf("hello(%d)\n",i); 
} 
void* world(void* arg){ //线程函数 
for(int i=0;i<300;i++)
printf("world(%d)\n",i); 
} 
int main() 
{ 
pthread_t tid1,tid2; 
pthread_create(&tid1, NULL, hello, NULL); 
pthread_create(&tid2, NULL, world, NULL); 
pthread_join(tid1, NULL); 
pthread_join(tid2, NULL);
printf("In main thread\n"); 
return 0; 
}

运行,可以发现效果并不好…
在这里插入图片描述
其原因和父子进程的原因差不多:时间片还没有耗尽时一个线程就已经结束了。我们修改一下实验代码


#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void *hello(void *arg)
{ //线程函数
    for (int i = 0; i < 3; i++){
        sleep(0.1);
        printf("hello(%d)\n", i);

    }
}
void *world(void *arg)
{ //线程函数
    for (int i = 0; i < 3; i++){
        sleep(0.1);
        printf("world(%d)\n", i);

    }
}
int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, hello, NULL);
    pthread_create(&tid2, NULL, world, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("In main thread\n");
    return 0;
}

重新编译运行:
在这里插入图片描述可以观察到对应的线程的并发执行。

实验六:线程共享变量

代码如下


#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
int global_value=100;
void *hello(void *arg)
{ //线程函数
    for (int i = 0; i < 3; i++){
        sleep(0.1);
        printf("hello(%d)\n", global_value++);

    }
}
void *world(void *arg)
{ //线程函数
    for (int i = 0; i < 300; i++){
        sleep(0.1);
        printf("world(%d)\n", global_value++);

    }
}
int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, hello, NULL);
    pthread_create(&tid2, NULL, world, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("In main thread,the global_value(%d)\n",global_value);
    return 0;
}

编译运行:

在这里插入图片描述可以看到,变量确实是共享的

Logo

鸿蒙生态一站式服务平台。

更多推荐