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

Linux进程初级:在以前的概念梳理中已经将进程的概念部分大体说明了,如今就是程序部分了。数组

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

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

如此,在运行了fork()函数后父子进程会运行同一个程序(因为代码段等彻底复制),所以须要用一种方法区分它们,不然,两个进程只能作同一间事。调试

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

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

#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

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

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

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

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

三、改变当前目录;chdir()函数用于改变当前工做目录。缘由:使用fork()建立的子进程继承了父进程的当前工做目录,因为在进程运行过程当中,工做目录是不能卸载的,这对之后的使用会形成麻烦,所以,一般的作法是让"/"做为守护进程的当前工做目录。

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

五、关闭文件描述符:同文件权限掩码同样,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);
}
相关文章
相关标签/搜索