Linux进程创建及同步实验(fork()函数使用,生产者-消费者问题的p,v操作)

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

=0:在子进程中

0:在父进程中

<0:创建失败

代码部分:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main(int argc,char *argv[])
{
	pid_t pid1,pid2;        //进程标识符
	pid1 = fork();     //创建一个新的进程
	if(pid1<0)
	{
		printf("创建进程失败!");
		exit(1);
	}
	else if(pid1==0)   //如果pid为0则表示当前执行的是子进程
	{
		printf("子进程1,进程标识符是%d\n",getpid());
	}
	else          //否则为父进程
	{
		pid2 = fork();//創建一個新的進
		if(pid2<0)
		{
			printf("创建进程失败!");
			exit(1);
		}
		else if(pid2==0)   //如果pid为0则表示当前执行的是子进程
		{
			printf("子进程2,进程标识符是%d\n",getpid());
		}
		else          //否则为父进程
		{
			printf("父进程,进程标识符是%d\n",getpid());
		}
	}
	return 0;
}

结果如上图

分析:在调用fork()函数创建子进程后,父子进程的执行顺序由操作系统来决定,相互之间没有任何时序上的关系,所以在我们没有加入进程同步机制的代码的情况下试图靠着调整语句的先后顺序来控制父子进程的先后循序是不可能的,所以在运行得到的结果中,我们看到每次运行的结果都有可能与上次的运行结果不同。

当然,细心的看官可能会发现:你的程序怎么父进程总是先执行呢?外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

答:之前说过父子进程的执行顺序完全由操作系统决定,对于我的机器而言,他偏偏是总是父进程先执行,那我有什么办法呢?(实验二就是办法!!),所以可以看到我的程序每次在执行时,总是先打印出父进程,而两个子进程的执行顺序完全时在多执行几次后就可以看到不同的顺序了。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实验二:修改上述程序,每一个进程循环显示一句话。子进程显示“duaghter…”及“son…”,父进程显示“parent…”,观察结果,分析原因。

这里,我在程序的每个进程中使用的for循环,循环显示一句话,循环次数为9次。

代码部分:

#include <stdio.h>
#include <stdlib.h>


int main(int argc,char *argv[])
{
	pid_t pid1,pid2;        //进程标识符
	pid1 = fork();     //创建一个新的进程
	if(pid1<0)
	{
		printf("创建进程失败!");
		exit(1);
	}
	else if(pid1==0)   //如果pid为0则表示当前执行的是子进程
	{
		for(int i=0;i<10;i++)
			printf("duaghter...,进程标识符是%d\n",getpid());
	}
	else          //否则为父进程
	{
		pid2 = fork();//創建一個新的進
		if(pid2<0)
		{
			printf("创建进程失败!");
			exit(1);
		}
		else if(pid2==0)   //如果pid为0则表示当前执行的是子进程
		{
			for(int i=0;i<10;i++)
				printf("son...,进程标识符是%d\n",getpid());
		}
		else          //否则为父进程
		{
			for(int i=0;i<10;i++)
				printf("parent...,进程标识符是%d\n",getpid());
		}
	}
	return 0;
}

运行结果图

分析:

gcc -o 编译完成后,./+可执行文件名执行后,多次重复执行,观察到每次结果都有所不同,for循环时操作系统的调用次序不同,显示的结果也不同。接着之前的分析,我们在调用fork()函数后可能每次执行都是父或子进程先执行,这也许会给我们制造一些困惑,难道我的程序出错了?难道书上讲错了?

当然不是,之所以实验一我们总看到父进程先执行,那是因为我们的程序执行太简单了!!!

每个进程就执行一个printf()这当然不合理,说明不了什么问题,所以基于这个问题,这次我将用一个for循环对每个进程的输出多执行几次,当然我们也可以用while(1)来一直执行,那样结果更加明显,当然最好设置一个键盘响应函数让按下某个键后可以退出。

从我执行得到的结果可以看出,现在,parent,duaghter,son的执行顺序就是随机的, 可能前一秒操作系统还在执行parent进程里面的代码,后一秒他就变了,开始执行son,或者duaghter里面的代码了。

实验进行到这我们也就可以更加深刻的了解fork()函数的来龙去脉了吧!

想要更加深入的了解fork()函数的使用可以参考网上的各种博客上关于fork()函数的解析!!!外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实验三:在调用exec()函数用新的程序替换该子进程的内容,并利用wait()函数来控制进程执行的顺序。调用exit()函数使子进程结束。

在fork后的子进程中使用exec函数族,可以装入和运行其它程序;

exec()函数说明:

实际上在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是:

#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, …);

int execlp(const char *file, const char *arg, …);

int execle(const char *path, const char *arg, …, char * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

(百度百科上解释)为防止大家对exec()函数族理解出错。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些函数具体用法大家可以亲自尝试一下,网上都有各种说明,在这不做过多说明;

在Linux中使用exec函数族主要有一下两种情况:

1,当进程认为自己不能再为系统和用户做出任何贡献调用exec函数族让自己重生

2,进程需要执行另一个程序。

wait()函数:

原理:进程一旦调用了wait,就立即阻塞自己,当分析到当前进程的子进程已经exit,便会收集这个子进程的信息,然后彻底销毁,如果没有找到这样的子进程,就会一直阻塞在这里,直到有一个出现。

在这里我调用execl()函数列出了路径为/bin/下的文件信息;两个子进程各自调用一次。

代码部分:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


	int main(int argc,char *argv[])
{
	pid_t pid1,pid2;        //进程标识符
	pid1 = fork();     //创建一个新的进程
	if(pid1<0)
	{
		printf("创建进程失败!");
		exit(1);
	}
	else if(pid1==0)   //如果pid为0则表示当前执行的是子进程
	{
		printf("子进程1,我将调用exec函数,进程标识符是%d\n",getpid());
		execl("/bin/ls","ls","-1","-color",NULL);
		printf("exec failed!\n");
		exit(1);
	}
	else          //否则为父进程
	{
		pid2 = fork();//創建一個新的進
		if(pid2<0)
		{
			printf("创建进程失败!");
			exit(1);
		}
		else if(pid2==0)   //如果pid为0则表示当前执行的是子进程
		{
			printf("进程2,我将调用exec函数,进程标识符是%d\n",getpid());
			execl("/bin/ls","ls","-1","-color",NULL);
			printf("exec failed!\n");
			exit(1);
		}
		else          //否则为父进程
		{
			wait(NULL);
			printf("父进程,进程标识符是%d\n",getpid());
			printf("完成!\n");
			exit(0);
		}
	}
	return 0;
}

运行结果:

分析:

可以看出在使用了exec()函数后程序使用了ls的命令,列出/bin/目录下的文件信息,执行完execl()函数后,子进程调用exit()函数,退出当前进程,而我们可以发现在使用wait()函数后,父进程永远将在其他的子进程完成之后才执行,所以在输出的结果中我们可以看到最后输出的将是父进程的信息。

实验四:利用Linux的信号量机制实现生产者消费者问题(基于进程)。

目的:利用生产者消费者问题更加深刻的理解进程同步与互斥的实现。

几个函数简介:

pthread_create():

函数简介

pthread_create是UNIX环境创建线程函数

头文件

#include<pthread.h>

函数声明

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

返回值

若成功则返回0,否则返回出错编号

参数

第一个参数为指向线程标识符的指针。

第二个参数用来设置线程属性。

第三个参数是线程运行函数的地址。

最后一个参数是运行函数的参数。

pthread_join():

函数简介

函数pthread_join用来等待一个线程的结束。

函数原型为:

extern int pthread_join __P (pthread_t __th, void **__thread_return);

参数:

第一个参数为被等待的线程标识符

第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。

注意

这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。如果执行成功,将返回0,如果失败则返回一个错误号。

signal():

SIGINT这个信号是在用户在控制台输入Ctrl+C的时候进程收到的。  
   
  signal(SIGINT,   &sig_int);这一句指定了收到了SIGINT这个信号以后,处理函数是sig_int

为了做好运维面试路上的助攻手,特整理了上百道 【运维技术栈面试题集锦】 ,让你面试不慌心不跳,高薪offer怀里抱!

这次整理的面试题,小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。

本份面试集锦涵盖了

  • 174 道运维工程师面试题
  • 128道k8s面试题
  • 108道shell脚本面试题
  • 200道Linux面试题
  • 51道docker面试题
  • 35道Jenkis面试题
  • 78道MongoDB面试题
  • 17道ansible面试题
  • 60道dubbo面试题
  • 53道kafka面试
  • 18道mysql面试题
  • 40道nginx面试题
  • 77道redis面试题
  • 28道zookeeper

总计 1000+ 道面试题, 内容 又全含金量又高

  • 174道运维工程师面试题

1、什么是运维?

2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

3、现在给你三百台服务器,你怎么对他们进行管理?

4、简述raid0 raid1raid5二种工作模式的工作原理及特点

5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

17、如何重置mysql root密码?

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

sh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

17、如何重置mysql root密码?

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值