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

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);
}

 

转载于:https://my.oschina.net/neverdead/blog/746984

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值