文档章节

Linux进程线程初探(进程的创建)

王千千
 王千千
发布于 2016/09/14 23:55
字数 1886
阅读 97
收藏 2

Linux进程初级:在之前的概念梳理中已经将进程的概念部分大致说明了,现在就是程序部分了。

环境:Ubentu 16.04.2(Vmware x_64) + gcc version 5.4.0

fork()函数用于从已存在的进程中创建一个新进程。新进程称为子进程,原进程称为父进程。使用fork()函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号处理函数、进程优先级、进程组号、当前工作目录、根目录、资源控制、控制终端等,子进程独有的是它的进程号、资源使用、计时器等。

如此,在运行了fork()函数后父子进程会运行同一个程序(由于代码段等完全复制),因此需要用一种方法区分它们,否则,两个进程只能做同一间事。

区分父子进程的方法是fork() 的返回值不同,父进程返回的是子进程的进程号,而子进程中返回0。

注意:子进程没有执行fork(),而是从fork()调用的下一条语句开始执行的。

#include <sys/types.h> //提供类型pid_t定义
#include <unistd.h>		//sleep()、fork()
#include <stdio.h>		//perror()、printf()

int main()
{
	pid_t ret;
	ret = fork();
	
	if(-1 == ret){
		perror("fork error:");
		return -1;
	}
	
	if(0 == ret){
		printf("child:%d PID:%d\n",ret,getpid());
		//getpid() returns the process ID of the calling process.
	}else{
		printf("parent:%d PID:%d\n",ret,getpid());
		usleep(100);
	}
	return 0;
}

执行结果:

parent:51477 PID:51476
child:0 PID:51477

从实例中可看出,fork()函数创建了一个子进程,其中父进程返回子进程的进程号,而子进程返回值为0。

exec函数族提供了一系列的在进程中执行另一程序的方法。exec可以根据指定的文件名或目录名找到可执行文件,并用它来代替当前程序的数据段、代码段、堆栈段。在执行完之后,当前进程除进程号外,其他内容都被替换了。这里的可执行文件即可以是二进制文件,也可以是Linux下任何可执行的脚本文件。

exec函数家族一共有6个,它们的功能都一致,只是使用的时候有细微的区别:表中国的前4个函数需要输入完整的路径,最后两个只要给出文件名,系统就会自动按照环境变量PATH所包含的路径进行查找;另外带l(list)的表示逐个列举参数的方式,其类型为const char *arg;字母v(vertor)表示通过指针数组传递,其类型为char *const argv[];字母e(environment)表示可在envp[]中指定当前进程的环境变量。

注:参数列表需以NULL结尾。

#include <sys/types.h> //提供类型pid_t定义
#include <unistd.h>		//sleep()、fork()
#include <stdio.h>		//perror()、printf()
#include <sys/wait.h> 	//wait()、waitpid()

int main()
{
	pid_t ret;
	ret = fork();
	
	if(-1 == ret){
		perror("fork error:");
		return -1;
	}
	
	if(0 == ret){
		if((ret = execlp("ls","ls","-l",NULL)) < 0){
			printf("execlp error\n");
		}
	}else{
		// while(wait(NULL) == 0);
		while(waitpid(ret,NULL,WNOHANG) == 0){
			printf("child process has not exited\n");
			usleep(100);
		}
		//WNOHANG为非阻塞模式,如果是0,父进程会一直阻塞,直到子进程结束。
		if(ret)
			printf("child exited\n");
		else
			printf("exit error\n");
	}
	return 0;
}

输出:

child process has not exited
child process has not exited
child process has not exited
child process has not exited
child process has not exited
child process has not exited
child process has not exited
-rwxrwxrwx 1 root root 8864 Sep 14 05:27 a.out
child process has not exited
-rwxrwxrwx 1 root root  695 Sep 14 05:27 execlp.c
child exited

交互式进程和批处理进程在程序上的差别只是交互式多了等待用户输入等与用户交互的动作。还有第三类进程值得我们注意 - 守护进程。

守护进程:这类进程一直在后台运行,很多系统进程都是以守护进程的形式存在。守护进程的实现有特定的步骤:

1、创建子进程,父进程退出:子进程变成孤儿进程被init收养。(在Ubentu的高级版本中会被 /sbin/upstart 进程收养,此进程为图形化的初始化程序,如在文本界面则没有此进程)

2、创建新会话:setsid()函数用于创建一个新的会话,并担任该会话组的组长。作用:让进程摆脱原会话组的控制;让进程摆脱原进程组的控制;让进程摆脱原控制终端的控制。原因:虽然父进程退出,但原先的会话期、进程组和控制终端并没有改变,因此,并不是真正意义上的独立。(进程组:进程组是一个或多个进程的集合。进程组由进程组ID来唯一标识,进程组ID也是一个进程的必备属性;会话期:会话组是一个或多个进程组的集合,通常,一个会话开始于用户登录,终止于用户退出;或者开始于终端打开,结束于终端关闭。会话期的第一个进程为会话组长,在此期间该用户运行的所有进程都属于这个会话期)

3、改变当前目录;chdir()函数用于改变当前工作目录。原因:使用fork()创建的子进程继承了父进程的当前工作目录,由于在进程运行过程中,工作目录是不能卸载的,这对以后的使用会造成麻烦,因此,通常的做法是让"/"作为守护进程的当前工作目录。

4、重设文件权限掩码;umask()设置文件权限掩码。作用是屏蔽文件权限中的对应位。例如,如果文件权限掩码是050,它表示屏蔽了文件组拥有着的可读与执行权限。fork()的子程序继承了父进程的文件权限掩码,这就给子进程使用文件带来了一定的影响。把文件权限掩码设置成0(umask(0)),可以增加守护进程的灵活性。

5、关闭文件描述符:同文件权限掩码一样,fork()子进程继承了父进程中的已经打开了的文件。这些被打开的文件可能永远不会被守护进程访问,但他们一样占用系统资源,而且还可能导致所在的文件系统无法被卸载。特别是守护进程与终端无关,所以指向终端设备的标准输入、标准输入和标准错误流已经失去了存在的价值,应当被关闭。

#include <sys/types.h> //提供类型pid_t定义
#include <unistd.h>		//sleep()、fork()
#include <stdio.h>		//perror()、printf()
#include <sys/wait.h> 	//wait()、waitpid()
#include <stdlib.h>		//exit(0)
#include <string.h>		//strlen()
#include <fcntl.h>		//open()、write()
#include <sys/stat.h>	//umask()
#include <syslog.h>		//日志后台调试 /var/log/syslog

int main()
{
	pid_t ret;
	ret = fork();
	
	int fd;
	char buf[] = "6";
	
	if(ret < 0){
		perror("fork error:");
		return -1;
	}else
	if(ret > 0){
		usleep(100);
		exit(0);	//父进程退出
	}
	
	
	openlog("daemon_syslog",LOG_PID,LOG_DAEMON);
	printf("pid:%d\n",getpid());
	if(setsid() < 0){
		syslog(LOG_ERR,"%s\n","setsid");
		exit(1);
	}	//创建新的会话
	if(chdir("/") < 0){
		syslog(LOG_ERR,"%s\n","chdir");
		exit(1);
	}		//改变工作目录
	umask(0);	//设置文件权限掩码
	int i,num = getdtablesize();	//获取当前进程文件描述符表大小
	for(i = 0;i < num;i++){	//循环关闭已打开文件
		close(i);
	}
	
	while(1){
		if((fd = open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600)) < 0){
			syslog(LOG_ERR,"%s\n","open");
			exit(1);
		}
		write(fd,buf,strlen(buf)+1);
		close(fd);
		sleep(2);
	}
	closelog();
	exit(0);
}

 

本文转载自:

共有 人打赏支持
王千千
粉丝 1
博文 15
码字总数 11187
作品 0
成都
程序员
私信 提问
1. Linux内核学习之进程和线程初探

1 进程 进程指的是处于执行期的程序。但是需要注意的是进程并不仅仅包括一段可执行程序的代码,它同时还包括其他资源,例如打开的文件,挂起的信号,内核内部数据,处理器状态,具有内存映射...

大风qixi
2017/12/29
0
0
Linux进程线程初探(概念)

多任务操作系统下通常有3个基本概念:任务、进程、线程。 任务:任务是一个逻辑概念,是为实现某一目的的一些列操作。一次任务可激发多个进程,这些进程相互合作来完成目标。 进程:指一个具...

王千千
2016/09/14
17
0
0-linux 环境编程修炼指南——外功心法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q1007729991/article/details/52770103 学习交流群: Linux 环境编程 610441700 说明:本系列文章并不能取代 ...

--Allen--
2016/10/09
0
0
Linux多线程并发服务器编程(线程池,FTP服务器)

分享网盘下载:https://pan.baidu.com/s/1gfNCcXt 密码: irfk 内容简介 本课程从最基础的进程、线程概念讲起逐步深入,通过理论与实践结合的方式,使学员快说掌握linux多线程网络编程技术,并...

人气王子333
2018/06/26
0
0
Linux内核学习笔记(1)-- 进程管理概述

一、进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码。通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映...

tongye
2018/08/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

day11

architect刘源源
今天
6
0
论学好Linux系统的超级重要性

不知道各位在日常的工作生活中有没有接触过“rm -rf /*”这个命令,因为这个命令搞出来的事情可还不少呢!前段时间就在一个群里看到了有个小伙子,老板让他去维护一下服务器,这小伙也不太懂...

Linux就该这么学
昨天
6
0
git 使用

1,首先在github配置好信息和仓库,然后在本地进行操作 git init git config user.name 'zhangwuer' git config user.email '56789053@qq.com' 2,与远程分支建立连接 git checkout -b test......

天王盖地虎626
昨天
3
0
git checkout 命令详解

在日常的git操作中,git checkout——检出,是我们的常用命令。最为常用的两种情形是创建分支和切换分支。 在下面的命令中,使用了一些简写,在这里说明一下: git st # git statusgit ci ...

shzwork
昨天
10
0
【Nginx】Nginx多级代理,获取客户端真实请求IP以及每级代理IP

Nginx多级代理,获取客户端真实请求IP以及每级代理IP 如图所示,每一级nginx里的location配置里需要加上对应的配置,最后一级nginx是直接到应用,测试时为了方便,直接用echo模块去测试,打印...

薛定谔的旺
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部