linux_linux- - CSDN
linux 订阅
Linux,全称GNU/Linux,是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年第一次释出,它主要受到Minix和Unix思想的启发,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux有上百种不同的发行版,如基于社区开发的debian、archlinux,和基于商业开发的Red Hat Enterprise Linux、SUSE、oracle linux等。 展开全文
Linux,全称GNU/Linux,是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年第一次释出,它主要受到Minix和Unix思想的启发,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux有上百种不同的发行版,如基于社区开发的debian、archlinux,和基于商业开发的Red Hat Enterprise Linux、SUSE、oracle linux等。
信息
创始人
林纳斯·托瓦兹
外文名
Linux
发布时间
1991年10月5日
发行版本
Debian、RedHat、Centos、Slackware、clearlinux.等
最新测试版本
5.8-rc1
最新版本
5.7.4
类    别
操作系统内核
特    点
免费、可靠、安全、稳定、多平台
更新时间
2020-04-12
linux简介
Linux,全称GNU/Linux,是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。伴随着互联网的发展,Linux得到了来自全世界软件爱好者、组织、公司的支持。它除了在服务器方面保持着强劲的发展势头以外,在个人电脑、嵌入式系统上都有着长足的进步。使用者不仅可以直观地获取该操作系统的实现机制,而且可以根据自身的需要来修改完善Linux,使其最大化地适应用户的需要。 [1]  Linux不仅系统性能稳定,而且是开源软件。其核心防火墙组件性能高效、配置简单,保证了系统的安全。在很多企业网络中,为了追求速度和安全,Linux不仅仅是被网络运维人员当作服务器使用,Linux既可以当作服务器,又可以当作网络防火墙是Linux的 一大亮点。 [2]  Linux 具有开放源码、没有版权、技术社区用户多等特点 ,开放源码使得用户可以自由裁剪,灵活性高,功能强大,成本低。尤其系统中内嵌网络协议栈 ,经过适当的配置就可实现路由器的功能。这些特点使得Linux成为开发路由交换设备的理想开发平台。 [3] 
收起全文
  • Linux从入门到系列课程,全部重点放在企业应用上面。课程主要针对互联网企业运维,因此不会花多余的时间去阐述无关紧要和企业用不到的东西,所以全程都是围绕实战运维,操作为主。
  • 人工智能、物联网、大数据时代,Linux正有着一统天下的趋势,几乎每个程序员岗位,都要求掌握Linux。本课程零基础也能轻松入门。 本课程以简洁易懂的语言手把手教你系统掌握日常所需的Linux知识,每个...
  • 本课程为全新马哥Linux全套系列课程之一--Linux基础入门和架构了解,从Linux起源,Linux架构和Linux形成历史开始逐步讲解,让你彻彻底底了解Linux的诞生,之后介绍了Linux相关文化和核心组成结构,以及Linux常用命令...
  • 课程主要面向嵌入式Linux初学者、工程师、学生 主要从一下几方面进行讲解: 1.linux学习路线、基本命令、高级命令 2.shell、vi及vim入门讲解 3.软件安装下载、NFS、Samba、FTP等服务器配置...
  • 1. Linux入门教程 http://c.biancheng.net/cpp/linux/ 基础教程 Linux简介 Linux文件管理 Linux目录 Linux文件权限和访问模式 Linux环境变量 Linux打印文件和发送邮件 Linux管道和过滤器 Linux进程...
    展开全文
  • (一)多进程并发服务器的编程 (1) 多进程并发服务器流程图

    (一)多进程并发服务器基础知识

    (1) 多进程并发服务器流程图

    在这里插入图片描述

    (2)涉及函数

    2.1 getopt_long()函数

    getopt_long()函数:长选项的命令行解析。
    目的:就是可以指定任意IP,PORT,避免经常在文件中修改。

    #include <getopt.h>		//头文件包含
    
    int getopt_long(int argc, char * const argv[],const char *optstring, const struct option *longopts,int *longindex);
    

    参数:
    argc和argv[]:用来接收main函数的参数。
    optstring:一个字符串,表示可以接受的参数。例如,“a:b:cd”,表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-a host -b name)。
    longopts:一个结构的实例。
    longindex:表示当前长参数在longopts中的索引值。
    示例

    getopt_long(argc,argv,"p:h",opts,NULL)
    

    2.1.1 option结构体

    struct option {
    const char *name; 
    int has_arg;
    int *flag;
    int val;
    }
    

    参数:
    name:表示的是长参数名
    has_arg(3个值):
    ①no_argument(或者是0),表示该参数后面不跟参数值
    ②required_argument(或者是1),表示该参数后面一定要跟个参数值
    ③optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值
    flag:用来决定,getopt_long()的返回值到底是什么。如果flag是null(通常情况),则函数会返回与该项option匹配的val值;如果flag不是NULL,则将val值赋予flag所指向的内存,并且返回值设置为0。
    val:与flag联合决定返回值
    示例

    struct option	opts[]={
    		{"port",required_argument,NULL,'p'},
    		{"help",no_argument,NULL,'h'},
    		{0,0,0,0}
    	};
    

    2.2 setsockopt()函数

    setsockopt()函数::用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。

    #include <sys/types.h>		//包含的头文件
    #include <sys/socket.h>		//包含的头文件
    
    int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
    
    参数 功能
    sockfd 指定套接字的描述符
    level 选项定义的层次。例如:SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6等
    optname 设置的选项名
    optval 指针,指向存放选项待设置的新值的缓冲区(布尔型/整型或结构选项)
    optlen optval缓冲区长度

    setsockopt有多种用法,这里我只讲与下面编程有关的用法。
    示例:

    int		on=1;
    setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    

    目的:当我们运行了程序,并退出后,端口并不会立即释放(会经理TIME_WAIT过程),此时使用setsockopt()则可以实现端口的重用。这样就不会出现经常看到的Address already in use现象了。
    setsockopt设置sockfd的用法可以参考下面的博客:
    https://blog.csdn.net/godleading/article/de8102814tails/

    (二)多进程并发服务器的编程

    (1) 用getopt_long实现长选项命令行解析

    #include<stdio.h>
    #include<errno.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<stdlib.h>
    #include<getopt.h>
    
    #define	MSG_STR	"Hello socket process world \n"
    //help printf
    void print_usage(char  *progname)
    {
    	printf("%s usage:\n ",progname);
    	printf("-p(--port):sepcify server listen port.\n");
    	printf("-h(--help):print this help information.\n");
    	return ;
    }
    int main(int argc,char **argv)
    {
    	int		sock_fd=-1;
    	int		cli_fd=-1;
    	int		rv=-1;
    	struct		sockaddr_in	servaddr;
    	struct		sockaddr_in	cliaddr;
    	socklen_t	len;
    	pid_t		pid;
    	int		ch;
    	int		on=1;
    	int		port=0;
    
    	//write a option to give help
    	struct option	opts[]={
    		{"port",required_argument,NULL,'p'},
    		{"help",no_argument,NULL,'h'},
    		{0,0,0,0}
    	};
    	
    	while((ch=getopt_long(argc,argv,"p:h",opts,NULL))!=-1)  
    	{
    		switch(ch)
    		{
    			case'p':
    				port=atoi(optarg);
    				break;       //不能少,要跳出循环
    			case'h':
    				print_usage(argv[0]);
    				return 0;
    		}
    	}
    	if(!port)
    	{
    		print_usage(argv[0]);
    		return 0;
    	}
    

    简析:这里简单来说就是做了提供帮助,指定端口的功能。

    (2) server创建的四步骤

    	//1.socket 
    	sock_fd=socket(AF_INET,SOCK_STREAM,0);
    	if(socket<0)
    	{
    		printf("Crate socket failure:%s\n",strerror(errno));
    		return -1;
    	}
    	else
    	{
    		printf("Create socket[%d],successfully!\n",sock_fd);
    	}
    	
    	setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//
    	
    	//2.bind
    	memset(&servaddr,0,sizeof(servaddr));
    	servaddr.sin_family=AF_INET;
    	servaddr.sin_port=htons(port);
    	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    
    	if(rv=bind(sock_fd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0);
    	{
    		printf("Socket[%d] bind on port [%d] failure:%s\n",sock_fd,port,strerror(errno));
    		return -2;
    	}
    	//3.listen
    	listen(sock_fd,13);	//队列
    	printf("Start to listen on port [%d]\n",port);
    
    	while(1)
    	{
    		printf("Start accept new client incoming..\n");
    		//4.accept
    		cli_fd=accept(sock_fd,(struct sockaddr *)&cliaddr,&len);
    		if(cli_fd<0)
    		{
    			printf("Accept new client failure:%s\n",strerror(errno));
    			return -3;
    		}
    		else
    		{
    			printf("Accept new client [%s:%d] suceesssfuly\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
    		}
    

    (3) 当接收客服端连接后创建子进程并进行操作

    //子进程:fork()  
    		pid=fork();
    		if(pid<0)	//创建子进程失败
    		{
    			printf("fork() crate child process failure :%s\n",strerror(errno));
    			close(cli_fd);
    			continue;
    		}
    		else if(pid>0)		//返回给父进程
    		{
    			close(cli_fd);
    			continue;
    		}
    		else if(0==pid)		//创建子进程中,并且进行I/O操作读取客户端cli_fd
    		{
    			char	buf[1024];
    
    			close(sock_fd);   //
    
    			printf("Child process start to commuicate with socket client..\n");
    
    			//IO操作读写操作
    			memset(buf,0,sizeof(buf));
    			rv=read(cli_fd,buf,sizeof(buf));
    			if(rv<0)
    			{
    				printf("read data from client sockfd[%d] failure:%s\n",cli_fd,strerror(errno));
    				close(cli_fd);
    				exit(0);		//退出
    			}
    			else if(rv==0)
    			{
    				printf("Socket[%d] get disconnected\n",cli_fd);
    				close(cli_fd);
    				exit(0);
    			}
    			else if(rv>0)
    			{
    				printf("read %d bytes data from server:%s\n",rv,buf);
    			}
    				
    			rv=write(cli_fd,MSG_STR,strlen(MSG_STR));
    			if(rv<0)
    			{
    				printf("write to client by sockfd[%d] failure:%d\n",sock_fd,strerror(errno));
    				close(cli_fd);
    				exit(0);
    			}
    
    			sleep(1);    //一般不这样处理
    
    			printf("close client socket[%d] and child process exit\n",cli_fd);
    			close(cli_fd);
    			exit(0);
    		}
    	}
    	close(sock_fd);
    	return 0;
    }
    

    (4) 没有客户端连接运行的结果

    ./socket_process_server -p 22
    

    在这里插入图片描述

    展开全文
  • (一)poll多路复用实现服务器多路并发 (1) poll()函数的基础知识 poll()函数 :和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列(不受1024个限制,但是随着个数上升效率会降低)。...

    (一)poll多路复用实现服务器多路并发

    (1) poll()函数的基础知识

    poll()函数 :和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列(不受1024个限制,但是随着个数上升效率会降低)。

    #include <poll.h>		//头文件包含
    
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    
    参数 功能
    struct pollfd *fds 用来指向一个struct pollfd结构类型的数组
    nfds_t nfds 指定数组中监听的元素个数
    int timeout 指定等待的毫秒数。(小于0:无限超时。等于0:指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件)

    返回值
    大于0:返回结构体中revents域不为0的文件描述符个数
    等于0:在超时前没有任何事件发生
    小于0:失败,同时会自动设置全局变量errno:

    参数 功能
    EBADF 一个或多个结构体中指定的文件描述符无效。
    EFAULTfds 指针指向的地址超出进程的地址空间。
    EINTR 请求的事件之前产生一个信号,调用可以重新发起。
    EINVALnfds 参数超出PLIMIT_NOFILE值。
    ENOMEM 可用内存不足,无法完成请求。

    1.1 struct pollfd结构类型数组

    struct pollfd
    {
    	int fd; 			//文件描述符 
    	short events; 		//等待的事件 
    	short revents; 		//实际发生了的事件 
    } ;
    

    poll函数可用的测试值:
    在这里插入图片描述

    (2) poll多路复用实现服务器多路并发

    2.1 服务器多路并发流程图

    在这里插入图片描述

    2.2 服务器多路并发代码

    2.2.1 长选项命令解析&判断命令参数的导入

    #include<stdio.h>
    #include<errno.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<ctype.h>
    #include<time.h>
    #include<pthread.h>
    #include<getopt.h>
    #include<libgen.h>		
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<netinet/in.h>
    #include<poll.h>
    
    #define ARRAY_SIZE(x)		(sizeof(x)/sizeof(x[0]))  //字节长除于首字节=个数
    
    int socket_server_init(char *listen_ip,int listen_port);
    static inline void print_usage(char *progname);		
    int main(int argc,char **argv)
    {
    	
    	int		daemon_run=0;
    	int		serv_port=0;
    	int		listen_fd;
    	char	*progname=NULL;
    	int		opt;
    	struct pollfd	fds_array[1024];  //poll 只能寻1024
    	int 		i,k;
    	int		conn_fd;
    	int		found;
    	int		max_fd=0;
    	char	buf[1024];
    	int		rv;
    	int		max;
    
    	//一、命令帮助提示
    	//另外:打印命令提示进行函数抽象	
    	progname=basename(argv[0]);	//从arg[0]中截取文件名
    	//1.opts结构体定义
    	struct option	opts[]=
    	{	
    		{"daemon",no_argument,NULL,'b'},	//守护进程
    		{"port",required_argument,NULL,'p'},
    		{"help",no_argument,NULL,'h'},
    		{0,0,0,0}
    	};
    	
    	//2.命令获取与解析
    	while((opt=getopt_long(argc,argv,"d:p:h",opts,NULL))!=-1)
    	{
    		switch(opt)
    		{
    			case'd':
    				daemon_run=1;
    				break;
    			case'p':
    				serv_port=atoi(optarg);
    				break;
    			case'h':
    				print_usage(progname);
    				return	EXIT_SUCCESS;
    			//出错
    			default:
    				break;
    		}
    	}
    	//3.判断输入值是否正确
    	if(!serv_port)
    	{
    		print_usage(progname);
    		return -1;
    	}
    	//判断是否需要守护进程(即是否要后台运行)
    	if(daemon_run)
    	{
    		daemon(0,0);
    	}
    
    	//判断服务器是否监听端口
    	if((listen_fd=socket_server_init(NULL,serv_port))<0)
    	{
    		printf("ERROR:%s server listen on port %d failure",argv[0],serv_port);
    		return -2;
    	}
    	//把监听到的listen_fd存放在数组中
    	for(i=0;i<ARRAY_SIZE(fds_array);i++)	//ARRAY_SIZE     
    	{
    		fds_array[i].fd=-1;		
    	}
    	fds_array[0].fd=listen_fd;
    	fds_array[0].events=POLLIN;
    	
    	max =0;
    

    2.2.2 poll()函数实现

    //二、poll()
    	for( ; ;)
    	{	
    		rv=poll(fds_array,max+1,-1);	//数组,元素个数,等待时间
    		if(rv<0)
    		{
    			printf("poll failure:%s\n",strerror(errno));
    			break;
    		}
    		else if(rv==0)
    		{
    			printf("poll get timeout\n");
    			continue;
    		}
    		
    		//用revents返回判断listen_fd是否以读。
    		if(fds_array[0].revents& POLLIN)	/
    		{
    			if((conn_fd=accept(listen_fd,(struct sockaddr *)NULL,NULL))<0)
    			{
    				printf("accept new client failure:%s\n",strerror(errno));
    				continue;
    			}
    			else 
    			{	
    				//判断当前连接客户端数量,是否达到上限
    				found=0;
    				for(i=0;i<ARRAY_SIZE(fds_array);i++)	
    				{	
    					
    					if(fds_array[i].fd<0)
    					{
    						printf("accept new client[%d] and add it into arry\n",conn_fd);
    						fds_array[i].fd=conn_fd;
    						found =1;
    						break;
    					}
    				}
    				if(!found)
    				{
    					printf("accepting a new client[%d] reach  the ceiling\n",conn_fd);
    					close(conn_fd);
    				}
    				max=i>max?i:max;
    				if(--rv<=0)
    				{
    					continue;
    				}
    			}
    		}
    		else //已连客户:1.数据请求 2.中断连接
    		{
    			 for(i=0;i<ARRAY_SIZE(fds_array);i++)
    			 {	
    				 //判断是否有已连接的conn_fd,判断这个conn_fd所对应events是否可读输入。
    				 if(fds_array[i].fd<0||!fds_array[i].events&POLLIN)
    				 {
    					 continue;	//结束循环
    				 }
    
    				 //IO操作
    				 if((rv=read(fds_array[i].fd,buf,sizeof(buf)))<=0)
    				 {
    					 printf("socket[%d] read failure or get disconnect.\n",fds_array[i]);
    					 close(fds_array[i].fd);
    					 fds_array[i].fd=-1;
    				 }
    				 else
    				 {
    					 printf("socket[%d] read get %d bytes data \n",fds_array[i],rv);
    					 //字符:小转大字母
    					 for(k=0;k<rv;k++)
    					 {
    						 buf[k]=toupper(buf[k]);
    					 }
    					 if(write(fds_array[i].fd,buf,rv)<0)
    					 {
    						 printf("socket[%d]write failure :%s\n",fds_array[i],strerror(errno));
    						 close(fds_array[i].fd);
    						 fds_array[i].fd=-1;
    					 }					 
    				 }
    			 }
    		}
    	}
    CleanUp:
    	close(listen_fd);
    	return 0;
    }
    

    2.2.3 命令提示打印函数

    static inline void print_usage(char *progname)		//了解static inlin 意义
    {
    	printf("Usage:%s [OPTION]...\n",progname);
    	printf("%s is a  socket server program\n",progname);
    	printf("Mandatory arguments:long or  short options\n");
    
    	printf("-b[daemon] set program running on background\n");
    	printf("-p[port] socket server port address\n");
    	printf("-h[help] display this help information\n");
    
    	printf("\nExample: %s -b -p 1111 \n",progname);
    	return ;
    }
    

    2.2.4 server四步骤

    //server四步
    int socket_server_init(char *listen_ip,int listen_port)
    {
    
    	int		on=1;
    	int		rv=0;
    	int		listen_fd;
    	struct sockaddr_in	servaddr;
    
    	//1.socket
    	if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
    	{
    		printf("socket () create a TCP socket failure:%s\n",strerror(errno));
    		return -1;
    	}
    	//端口重用
    	setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    
    	//初始化操作
    	memset(&servaddr,0,sizeof(servaddr));
    	servaddr.sin_family=AF_INET;
    	servaddr.sin_port=htons(listen_port);
    
    	//判断IP地址,并初始化。意义:这里if else相当于两用。监听特定IP,或监听所有IP。
    	if(!listen_ip)
    	{
    		servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    		printf("listen other IP address [%d]",listen_ip);
    	}
    	else 
    	{
    		if(inet_pton(AF_INET,listen_ip,&servaddr.sin_addr)<=0)
    		{
    			printf("inet_pton() set listen IP address failure.\n");
    			rv=-2;
    			goto CleanUp;
    		}
    	}
    
    	//2.bind
    	if(bind(listen_fd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    	{	
    		printf("bind() bind the TCP socket failure: %s\n",strerror(errno));
    		rv=-3;
    		goto CleanUp;
    	}
    	//3.listen
    	if(listen(listen_fd,13)<0)
    	{
    		printf("bind() bind the TCP socket failure: %s\n",strerror(errno));
    		rv=-4;
    		goto CleanUp;
    	
    	}
    CleanUp:
    	if(rv<0)
    	{
    		close(listen_fd);
    	}
    	else
    	{
    		rv=listen_fd;
    		return rv;
    	}
    }
    
    展开全文
  • (一)多线程基本知识

    (一)多线程基本知识

    为什么要学习线程呢?之前我们不是学了进程吗?通过创建进程一样可以实现多个程序的并发运行。那么我们学习线程的意义在于什么?
    意义
    假设,在我们完成相关任务的不同代码间需要交换数据时候。
    如果采用多进程并发方式:
    ①进程创建比线程创建花费时间多
    ②进程之间通信比较麻烦
    采用多线程方式:
    ①共享全局变量,线程间数据交换会比较高效。

    (1) 什么是线程?多线程?

    线程:是操作系统能够进行运算调度的最小单位,是进程的一条执行路径,在Unix系统下也被称为轻量级进程(轻量进程更多指内核线程,而把用户线程称为线程)。所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源。
    多线程:一个进程可以有很多线程,每条线程并行执行不同的任务。
    在这里插入图片描述

    (2)多线程中的主线程&子线程

    就像我们多进程一样,我们有父进程,有子进程。那么多线程也一样,有主线程,有子线程等。

    2.1 主线程

    主线程:一个进程创建后,会首先生成一个缺省的线程,称为主线程。

    2.2 子线程

    子线程:由主线程调用pthread_create()创建的线程。
    特点
    ①有自己的入口函数,该函数由用户在创建的时候指定。
    ②每个线程都有自己的线程ID,可以通过pthread_self()函数获取。

    2.3 主线程与子线程的关系

    主线程和子线程的默认关系:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。
    产生的问题
    ①当进程结束或僵死后,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。(这就像工厂要关门了,结果发现里面还有工人,他们还没有完成任务。这时工厂如果要关门,只有把它们赶走后才能关门)
    ②线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。(这就像工厂要关门了,催促工人赶紧走,但是有些工人急急忙忙走后,但运行的机器没有被关闭)
    所以,我们需要提前设置线程属性去解决这些问题。

    2.3.1 可会合

    可会合:主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。(简单来说:就是上面提到的,工厂需要确保工人和机器都关闭了再关门)

    2.3.2 相分离

    相分离:表示子线程无需和主线程会合,也就是相分离的。这种情况下,子线程一旦进入终止状态,这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。(简答来说:工厂虽然关门了,但是让工人们完成自己任务后,自己从后门出去(释放系统资源))

    (3) 多线程中常用函数

    3.1 pthread_create()函数

    pthread_create()函数:创建一个线程。

    #include <pthread.h>		//头文件包含
    
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void*arg);
    
    参数 功能
    *thread 指向线程标识符的指针 (返回线程ID)
    pthread_attr_t*attr 设置线程属性。
    start_routine 线程运行函数的起始地址
    arg 运行函数的参数

    详解:void *(*start_routine) (void *)
    定义函数指针: start_routine
    返回值:void *(一个指针)
    参数:void *(一个指针)
    pthread_attr_t定义

    typedef struct
    {
    	int detachstate; 					//线程的分离状态
    	int schedpolicy; 					//线程调度策略
    	struct sched_param schedparam; 		//线程的调度参数
    	int inheritsched; 					//线程的继承性
    	int scope; 							//线程的作用域
    	size_t guardsize; 					//线程栈末尾的警戒缓冲区大小
    	int stackaddr_set;
    	void * stackaddr; 					//线程栈的位置
    	size_t stacksize; 					//线程栈的大小
    }pthread_attr_t;
    

    3.1.1 pthread_attr_init ()函数

    pthread_attr_init ()函数:初始化一个线程对象的属性。
    注意:一般调用了pthread_attr_init()函数初始化线程属性后,要用pthread_attr_destroy()函数对其去初始化

    int pthread_attr_init(pthread_attr_t *attr);
    

    参数:要初始化线程属性结构的指针

    3.1.2 pthread_attr_destroy()函数

    pthread_attr_destroy()函数:销毁一个目标结构,并且使它在重新初始化之前不能重新使用。

    int pthread_attr_destroy(pthread_attr_t *attr);
    

    参数:要删除的线程属性结构体指针

    3.1.3 pthread_attr_setstacksize()函数

    int pthread_attr_setstacksize()函数:设置线程堆栈大小

    int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
    

    默认堆栈大小: 8388608; 堆栈最小:16384 。单位:字节

    3.1.4 pthread_attr_setdetachstate()函数

    pthread_attr_setdetachstate()函数:设置线程关系是可会合还是相分离(默认:可会合)

    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);		//设置为相分离
    

    3.2 pthread_join()函数

    pthread_join()函数:等待一个线程的结束,线程间同步的操作。
    注意:一个线程不能被多个线程等待,也就是说对一个线程只能调用一次pthread_join,否则只有一个能正确返回,其他的将返回ESRCH 错误。

    #include<pthread.h>		//头文件包含
    
     int pthread_join(pthread_t thread, void **retval);
    
    参数 功能
    thread 线程ID
    retval 用户定义的指针,用来存储被等待线程的返回值

    示例:

    pthread_join(tid, NULL);
    

    3.3 pthread_exit()函数

    pthread_exit()函数:终止调用它的线程并返回一个指向某个对象的指针。

    void pthread_exit(void* retval);
    pthread_exit()
    

    (二)多线程编程

    目的:用简单例子讲解多线程创建基本使用,并且了解子线程共享主线程的数据。

    (1) 设置线程属性

    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    void *thread_worker1(void *args);
    void *thread_worker2(void *args);
    int main(int argc, char **argv)
    {
    	int shared_var = 1000;			//导入线程的参数
    	pthread_t tid;					//线程ID
    	pthread_attr_t thread_attr;		//线程属性
    
    	//一、设置线程属性
    	//初始化线程属性
    	if(pthread_attr_init(&thread_attr))
    	{
     		printf("pthread_attr_init() failure: %s\n", strerror(errno));
     		return -1;
    	}
    
    	//设置线程堆栈大小
    	if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
           	{
     		printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
     		return -2;
    	}
    	//设置线程关系:分离还是会合  默认是会合,这里设置是分离
    	if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
    	{
    	 	printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
    	 	return -3;
    	}
    

    (2) 创建子线程,并用while让主线程不断工作

    	//二、创建线程
    	//线程1
    	pthread_create(&tid, &thread_attr, thread_worker1, &shared_var);	//线程ID 线程属性
    	printf("Thread worker1 tid[%ld] created ok\n", tid);
    	//线程2
    	pthread_create(&tid, NULL, thread_worker2, &shared_var);
    	printf("Thread worker2 tid[%ld] created ok\n", tid);
    	//销毁线程
    	pthread_attr_destroy(&thread_attr);
    	
    	//等待线程2退出再执行下面代码
    	pthread_join(tid, NULL);
    
    	//主线程工作
    	while(1)
    	{
    		printf("Main/Control thread shared_var: %d\n", shared_var);
    		 sleep(10);
    	}
    }
    

    简析:
    ①调用pthread_attr_destroy():对其去除初始化(pthread_attr_init)
    ②调用pthread_join():因为worker1设置了相分离的关系,所以并不会返回主线程。所以,这里pthread_join()函数等待的是worker2结束。

    (3) 子线程运行函数

    void *thread_worker1(void *args)
    {
        int *ptr = (int *)args;
    	if( !args )
    	{
    		printf("%s() get invalid arguments\n", __FUNCTION__);
    		pthread_exit(NULL);	//若用exit会退出进程
    	}
    
    	printf("Thread workder 1 [%ld] start running...\n", pthread_self());
    	//子线程工作
    	while(1)
    	{
    		printf("+++: %s before shared_var++: %d\n", __FUNCTION__, *ptr);
    		*ptr += 1;
    		sleep(2);
    		printf("+++: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
    	}
    	printf("Thread workder 1 exit...\n");
    	return NULL;
    }
    void *thread_worker2(void *args)
    {
    	int *ptr = (int *)args;
    	if( !args )
    	{
    		printf("%s() get invalid arguments\n", __FUNCTION__);
    		pthread_exit(NULL);
    	}
    	//子线程工作
    	printf("Thread workder 2 [%ld] start running...\n", pthread_self());
    	while(1)
    	{
    		printf("---: %s before shared_var++: %d\n", __FUNCTION__, *ptr);
    		*ptr += 1;
    		sleep(2);
    		printf("---: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
    	}
    	printf("Thread workder 2 exit...\n");
    
    	return NULL;
    }
    

    简析:
    ①这里__FUNCTION__作用是:获取函数名。

    (4) 编译代码并执行程序

    gcc thread.c -o thread -lpthread
    ./thread
    

    (5) 结果显示

    在这里插入图片描述

    展开全文
  • 1、 在操作系统原理的术语中,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进 程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将...

    1、 线程介绍

    在操作系统原理的术语中,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
    但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境 (register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

    2、线程的创建

    一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),C/C++程序中,主线程就是通过 main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户 在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊 之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。

    无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有 子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁 后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资 源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销 毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:
    1. 可会合(joinable):
    这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线 程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函 数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否 则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
    2. 相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种 方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是 很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

    线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程 等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其 他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

    3、多线程服务器编程代码:

    #ifndef __SOCKET_SERVER_H //防止重重定义
    #define __SOCKET_SERVER_H
    #include <sys/types.h>      
    #include <sys/socket.h>
    #include <string.h>
    #include<stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <getopt.h>
    #include <pthread.h>
    #include <ctype.h>
    #define	MSG_STR  "Hello network word! I'm sever!"
    #define	BUF_SIZE  1024
    #endif
    
    typedef void *(WORKWER_BODY) (void *thread_arg); //定义函数指针
    void *thread_worker(void *ctx);
    int thread_start(pthread_t * thraed_id, WORKWER_BODY* thread_workbody, void * thread_arg);
    
    void print_usage(char *prograname)
    {
        printf("%s usage : \n", prograname);
        printf("-p(--port): specify sever listen port.\n");
        printf("-h(--help): print this help information.\n");
    
        return  ;
    
    }
    int main (int argc, char **argv)
    {
    
        int                      sockfd;
        int                      rv = -1;
        int                      clifd;
        struct sockaddr_in       servaddr;
        struct sockaddr_in       cliaddr;
        socklen_t                len = sizeof(cliaddr);
        int                      port    = 0;
        int                      ch;
        int                      on = 1;
        pthread_t                tid;
    
        struct option        opts[] = {
            {"port", required_argument, NULL, 'p'},            
            {"help", no_argument, NULL, 'h'},          
            {NULL, 0, NULL, 0}
    
        };
    
        while((ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )  //实现参数解析
        {
            switch(ch)
            {
                case 'p':
                    port=atoi(optarg);
                    break;
                case 'h':
                    print_usage(argv[0]);
                    return 0;
            }           
        }
    
        if( !port )        
        {
            print_usage(argv[0]);
            return 0;        
        }
    
        sockfd = socket(AF_INET,SOCK_STREAM,0); //服务器第一步,socket();
        if(sockfd < 0)
        {
            printf("Cearte socket failure :%s\a\n",strerror(errno));
            return -1;
        }
        printf("Create socket[%d] sucessfully.\n",sockfd);
    
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on,sizeof(on));//使得端口号在短时间内可以得到调用
    
        memset(&servaddr,0,sizeof(servaddr)); //清空servaddr结构体
        servaddr.sin_family = AF_INET;    //设置ipv4协议,如果要设置ipv6协议, 则需AF_INET6;
        servaddr.sin_port = htons(port);   //将本地字节序的端口转化为网络字节序
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //可以监听任何访问IP地址
    
        rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); //服务器第二步,bind();
        if(rv < 0 )
        {
            printf(" socket[%d] bund port[%d] failure :%s\n\a",sockfd, port,strerror(errno));
            return -2;
        }
    
        listen(sockfd,13);  //服务器第三步,listen;
        printf("Statring to listen on port[%d].\n",port);
    
        while(1)
        {
            printf("Starting to accept new client incomming...\n");
    
            clifd = accept(sockfd, (struct sockaddr *)&cliaddr,&len); //服务器第四步,accept(),本处会从在阻塞
            if(clifd < 0)
            {
                printf("Accept new client failure :%s\n\a", strerror(errno));
            }
            printf("Accept new client[%s:%d]successfuly\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
    
            thread_start(&tid, thread_worker,(void *)(long)clifd ); //创建新的线程来解决accept阻塞问题
        }
        close(sockfd);
    
        return 0;
    }
    
    int thread_start(pthread_t * thread_id, WORKWER_BODY* thread_workbody, void * thread_arg)
    {
        int                rv = -1;
        pthread_attr_t      thread_attr;
        if(pthread_attr_init(&thread_attr))  //初始化线程属性 thread_attr
        {
            printf("pthread_attr_init() failure:%s\n", strerror(errno));
            goto CleadUp;
        }
    
        if(pthread_attr_setstacksize(&thread_attr, 12*1024)) //设置栈大小
        {
            printf("pthread_attr_setstacksize() failure:%s\n", strerror(errno));
            goto CleadUp;   
        }
    
        if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED)) //设置可分离的状态
        {
            printf("pthread_attr_detachstate() failure:%s\n", strerror(errno));
            goto CleadUp;   
        }
    
        if(pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg)) //创建子线程
        {
            printf("pthread_create() failure:%s\n", strerror(errno));
            goto CleadUp;   
        }
    
        rv = 0;
    CleadUp:
        pthread_attr_destroy(&thread_attr); //摧毁线程属性,释放占用资源
        return rv;
    }
    
    void *thread_worker( void *ctx) //服务器第五步 read()会阻塞, write();
    { 
        int        clifd = 0;
        char       buf[1024];
        int        rv = 0;
        if(!ctx) 
        {
            printf("Invalid input arguements in %s()\n", __FUNCTION__);
            pthread_exit(NULL);   
        }
        clifd = (long)ctx;
        printf("clifd = %d\n",clifd );
        printf("Child thread start to commuicate with socket client ...\n");
        while (1) 
        {
            memset(buf,0,sizeof(buf));
            rv = read(clifd,buf,sizeof(buf));
            if(rv < 0)
            {
                printf("Read from client socket[%d] failure: %s\n",clifd,strerror(errno));
                close(clifd);
                pthread_exit(NULL);
            }
            else if(rv == 0)
            {
    
                printf("socket[%d] get disconnected\n",clifd);
                close(clifd);
                pthread_exit(NULL); //子线程退出必须调用pthread_exit(),调用exit()或return 都会使父子线程都结束
            }
            else if(rv >0)
            {
                printf("Read %d bytes data form client:\n%s\n",rv,buf);
            }
            rv = write(clifd,MSG_STR,strlen(MSG_STR));
            if(rv < 0)
            {
                printf("write to client by socket[%d] failure :%s.\a\n",clifd,strerror(errno));
                close(clifd);
                pthread_exit(NULL);
            }
        }
    }
    
    展开全文
  • (一)多线程服务器的基础知识 (1) 多线程服务器的流程图 (2) 涉及函数
  • 文章目录Linux 概述什么是LinuxUnix和Linux有什么区别?什么是 Linux 内核?Linux的基本组件是什么?Linux 的体系结构BASH和DOS之间的基本区别是什么?Linux 开机启动过程?Linux系统缺省的运行级别?Linux 使用的...
  • linux中的&&和&,|和||

    2017-05-24 18:28:21
    linux中,&和&&,|和||介绍如下: & 表示任务在后台执行,如要在后台运行redis-server,则有 redis-server & && 表示前一条命令执行成功时,才执行后一条命令 ,如 echo '1‘ && echo '2' | 表示管道,上一条...
  • Linux入门基础教程

    2018-05-29 17:52:25
    1. 1 Linux操作系统简介Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64...
  • Linux存储是以挂载的方式,相当于是树状的,源头就是”/“,也就是根目录。而每个用户都有”家“目录,也就是用户的个人目录,比如root用户的”家“目录就是/root,普通用户a的家目录就是/home/a.可以看到用户创建完...
  • Linux 免费或少许费用。 2. 软件与支持 Windows 平台:数量和质量的优势,不过大部分为收费软件;由微软官方提供重要支持和服务; Linux 平台:大都为开源自由软件,用户可以修改定制和再发布,由于基本免费没有资金...
  • Linux简介Linux文件管理Linux目录Linux文件权限和访问模式Linux环境变量Linux打印文件和发送邮件Linux管道和过滤器Linux进程管理Linux网络通信工具vi编辑器Linux文件系统Linux文件存储结构Linux用户管理Linux系统...
  • 安装好之后,不会在系统菜单里显示出什么东软件busybox(Linux命令盒)使用教程linux教程西。如果安装了TitaniumBackup,不会再...  busybox是一个工具,里面提供了很多Linux的命令,可以用来做很多事,比如app2sd...
  • Linux就该这么学》- 必读的Linux系统与红帽认证自学手册点此阅读:http://www.linuxprobe.com/Linux技术交流社区:http://bbs.linuxprobe.com/本书是由全国多名红帽架构师(RHCA)基于RHEL7系统共同编写的高质量...
  • 鸟哥的Linux私房菜-基础篇.第四版.pdf 下载 Linux_操作系统-基础操作-教学.doc 下载 linux内核深入剖析基于0.11.pdf 下载 Linux系统命令及其使用详解.doc 下载 Ubuntu_Linux从入门到精通....
  • VMware安装Linux教程

    2019-06-25 18:26:47
    1.创建新的虚拟机 2.典型→下一步 3.选择【稍后安装操作系统】,点击【下一步】 4.选择【稍后安装操作系统】,点击【下一步】 5.虚拟机名称输入【CentOS 7 64位】,点击【浏览】更改系统的安装位置...7...
1 2 3 4 5 ... 20
收藏数 276,657
精华内容 110,662
关键字:

linux