[apue]多进程编程

1.多进程编程基础知识

(1)什么是进程?

在操作系统原理使用这样的术语来描述的:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进 程。站在程序员的角度来看,我们使用vim编辑生成的C文件叫做源码,源码给程序员来看的但机器不识别,这时我们需要使用 编译器gcc编译生成CPU可识别的二进制可执行程序并保存在存储介质上,这时编译生成的可执行程序只能叫做程序而不能叫进 程。而一旦我们通过命令(./a.out)开始运行时,那正在运行的这个程序及其占用的资源就叫做进程了。

(2)进程空间内存布局

linux下进程的内存分布
栈:栈内存由编译器在程序编译阶段完成,进程的栈空间位于进程用户空间的顶部并且是向下增长,每个函数的每次调用 都会在栈空间中开辟自己的栈空间,函数参数、局部变量、函数返回地址等都会按照先入者为栈顶的顺序压入函数栈中, 函数返回后该函数的栈空间消失,所以函数中返回局部变量的地址都是非法的。
堆:堆内存是在程序执行过程中分配的,用于存放进程运行中被动态分配的的变量,大小并不固定,堆位于非初始化数据 段和栈之间,并且使用过程中是向栈空间靠近的。当进程调用 malloc 等函数分配内存时,新分配的内存并不是该函数的 栈帧中,而是被动态添加到堆上,此时堆就向高地址扩张;当利用 free 等函数释放内存时,被释放的内存从堆中被踢 出,堆就会缩减。因为动态分配的内存并不在函数栈帧中,所以即使函数返回这段内存也是不会消失。
非初始化数据段:通常将此段称为 bss 段,用来存放未初始化的全局变量和 static 静态变量。并且在程序开始执行之前, 就是在 main()之前,内核会将此段中的数据初始化为 0 或空指针。
初始化数据段:用来保已初始化的全局变量和 static 静态变量。 文本段也称代码段,这是可执行文件中由 CPU 执行的机器指令部分。正文段常常是只读的,以防止程序由于意外而修改 其自身的执行。

(3)四次握手模型

四次握手模型示意图

2.实现函数

(1)fork()系统调用

fork()系统调用会创建一个新的子进程,这个子进程是父进程的一个副本。这也意味着,系统在创建新的子进程成功后,会将 父进程的文本段、数据段、堆栈都复制一份给子进程,但子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程 空间的相应内存。这时系统中出现两个基本完全相同的进程(父、子进程),这两个进程执行没有固定的先后顺序,哪个进程先执 行要看系统的进程调度策略。如果需要确保让父进程或子进程先执行,则需要程序员在代码中通过进程间通信的机制来自己实 现。

(2)exec*()执行另外一个程序

在fork()之后紧接着调用exec*()系列的函数来让子进程去执行另外一个程序。其中exec*()是一些列的函数。

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[]);  

(3)vfork()系统调用

vfork()是另外一个可以用来创建进程的函数,他与fork()的用法相同,也用于创建一个新进程。 但vfork()并不将父进程的地址 空间完全复制到子进程中,因为子进程会立即调用exec或exit(),于是也就不会引用该地址空间了。不过子进程再调用exec()或 exit()之前,他将在父进程的空间中运行,但如果子进程想尝试修改数据域(数据段、堆、栈)都会带来未知的结果,因为他会影响 了父进程空间的数据可能会导致父进程的执行异常。此外,vfork()会保证子进程先运行,在他调用了exec或exit()之后父进程才 可能被调度运行。如果子进程依赖于父进程的进一步动作,则会导致死锁。

(4)system()与popen()函数

3.实现代码

在这里插入图片描述
源码链接
https://gitee.com/old_man_with_horse_face/temperature_sensor_project

(1)服务器端

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sqlite3.h>

#define BACKLOG 10
#define MSG_STR "Hello, you have connected to the server successfully!\n\n"

int g_stop = 0;

void sig_stop(int signum)
{
     	if (SIGINT == signum)  
     	{
      		printf("SIGINT signal detected\n");
         	g_stop = 1;
 	}
}


int main(int argc, char *argv[])
{
     	int     			rv;
     	int                      	port = 8889;
     	int                      	on = 1;
     	int                      	server_fd = -1;
     	int                      	client_fd = -1;
     	pid_t                    	pid = -1;
     	struct sockaddr_in       	server_addr;
     	struct sockaddr_in       	client_addr;
     	socklen_t                	clientaddr_len = 20;
 	int    				daemon_start = 1;
 	int    				log_fd;
 	char    			sql[128];
 	char     			*errmsg;
 	sqlite3    			*db;
 	if((sqlite3_open("temperature.db", &db)) != SQLITE_OK)
 	{
  		printf("Open database unsuccessfully : %s\n", sqlite3_errmsg(db));
  		exit(1);
 	}
 	else
 	{ 
  		printf("Open database successfully!\n");
 	}
 	if((sqlite3_exec(db, "create table if not exists temperature(infor varchar(100));", NULL, NULL, &errmsg)) != SQLITE_OK)
 	{
  		printf("Create table unsuccessfully : %s\n", errmsg);
  		exit(1);
	}
 	else
 	{
  		printf("Create table successfully!\n");
   	}
   	
     	signal(SIGINT, sig_stop);
     	server_fd = socket(AF_INET, SOCK_STREAM, 0);
     	if (server_fd < 0)
     	{
         	printf("Creat socket unsuccessfully : %s\n", strerror(errno));
         	return 0;
     	}
    	printf("Creat the server[%d] successfully!\n",server_fd);
    	
     	memset(&server_addr, 0, sizeof(server_addr));
     	server_addr.sin_family = AF_INET;  
     	server_addr.sin_port = htons(port); 
     	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     	
     	setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); 
     	
	rv = bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     	if (rv < 0)
     	{
         	printf("Bind server's port unsuccessfully:%s\n", strerror(errno));
         	return 0;
     	}
     	printf("The port[%d] has been binded by this server!\n", port);
     	
     	listen(server_fd, BACKLOG);
 	if ( !daemon_start )
 	{
  		printf("Program is running in the background!\n");
  		log_fd = open("temperature.log", O_CREAT|O_RDWR, 777);
  		if (log_fd < 0)
  		{
   			printf("Open temperature.log unsuccessfully : %s", strerror(errno));
   			return 0;
  		}
  		dup2(log_fd, STDOUT_FILENO);
  		dup2(log_fd, STDERR_FILENO);
  		if((daemon(1,1)) < 0)
  		{
   			printf("Create daemon unsuccessfully : %s", strerror(errno));
   			return 0;
  		}
	}
	
     	while (!g_stop)
     	{ 
         	client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &clientaddr_len);
         	if (client_fd < 0)
         	{
              		printf("Accept request from client unsuccessfully : %s\n", strerror(errno));
              		continue;
         	}
         	printf("Accept request from client[%s:%d] successfully!\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
         	
         	pid = fork();  
         	if (pid < 0)
         	{
              		printf("Create child process unsuccessfully : %s.\n", strerror(errno));
   			close(client_fd);
              		continue;
         	}
         	else if (pid > 0)
         	{
              		close(client_fd); 
              		continue;
         	}
         	else if (pid == 0)
         	{
   			char   buf[1024];
              		close(server_fd); 
  			while(1)
              		{
                  		memset(buf, 0, sizeof(buf));               
                  		rv = read(client_fd, buf, sizeof(buf)); 
                  		if (rv < 0)  
                 		{
                       			printf("Read information from client unsuccessfully : %s\n", strerror(errno));
                       			close(client_fd);
     exit(0);
                  		}
          			else if (rv == 0)  
                  		{
                       			printf("The client[%d] get disconnected!\n", client_fd);
                       			close(client_fd);
     exit(0);
                  		}
                  		else  
                  		{
                       			printf("The message recived from client[%s,%d] : \n%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf);
     					sprintf(sql,"insert into temperature values('%s');", buf);
     					if((sqlite3_exec(db, sql, NULL, NULL, &errmsg)) != SQLITE_OK)
     					{
      						printf("Insert data into table unsuccessfully : %s\n", errmsg);
     					}
     					else
     					{
      						printf("Insert data into table successfully!\n\n");
     					}
                  		}
                  		
	rv = write(client_fd, MSG_STR, strlen(MSG_STR));
    	if (rv < 0)
    	{
     		printf("Write to client unsuccessfully : %s\n", strerror(errno));
     		close(client_fd);
    	}
       			}
     		}
     	}
 	close(server_fd);
     	return 0;
}

(2)客户端

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<getopt.h>
#include<netdb.h>
#include<dirent.h>
#include<signal.h>
#include<syslog.h>
#include<time.h> 
#include<strings.h>

int g_stop = 0;
void sig_handler(int signum);
void sig_code(int signum);
void dns(char *domain_name, char **ip);
int get_sys_time(char *datime);
int ds18b20_get_temperature(float *temp); 

void sig_handler(int signum)
{
 	if(SIGTERM==signum)
 	{
  		printf("SIGTERM signal detected\n");
  		g_stop=1; 
 	}
 	else if(SIGALRM==signum)
	{
  		printf("SIGALRM signal detected\n");
  		g_stop=1;
 	}
 	else if(SIGINT==signum)
 	{
  		printf("SIGINT signal detected\n");
  		g_stop=1;
 	}
}

void sig_code(int signum)
{
 	if(SIGBUS==signum)
 	{
  		printf("SIGBUS signal detected\n");
 	}
 	else if(SIGILL==signum)
 	{
  		printf("SIGILL signal detected\n");
 	}
 	else if(SIGSEGV==signum)
 	{
  		printf("SIGSEGV signal detected\n");
 	}
 	exit(1);
}

int get_sys_time(char *datime)
{
 	time_t seconds;
 	struct tm *pTM;

 	time(&seconds);
 	pTM=localtime(&seconds);
 	sprintf(datime,"%04d-%02d-%02d  %02d:%02d:%02d",pTM->tm_year+1900,pTM->tm_mon+1,pTM->tm_mday,pTM->tm_hour,pTM->tm_min,pTM->tm_sec);
 	return 0; 
}

int ds18b20_get_temperature(float *temp)
{
 	char  w1_path[50] = "/sys/bus/w1/devices/";
 	char  chip[20];
 	char  buf[128];
 	DIR  *dirp;
 	struct dirent *direntp;
 	int   fd = -1;
 	char  *ptr;
 	float  value;
 	int  found = 0;
 	if( !temp )
 	{
  		return -1;
 	}
 	if( (dirp = opendir(w1_path)) == NULL )
 	{	
  		printf("opendir error : %s\n", strerror(errno));
 	}
 	while( (direntp = readdir(dirp)) != NULL )
 	{
  		if(strstr(direntp->d_name, "28-"))
  		{
   			strcpy(chip, direntp->d_name);
   			found = 1;
   			break;
  		}
 	}
 	closedir(dirp);
 	if( !found )
 	{
  		printf("Can not find ds18b20 in %s\n", w1_path);
  		return -3;
 	}
 	strncat(w1_path, chip, sizeof(w1_path) - strlen(w1_path));
 	strncat(w1_path, "/w1_slave", sizeof(w1_path) - strlen(w1_path));
 	if( (fd = open(w1_path, O_RDONLY)) < 0 )
 	{
  		printf("open %s error : %s\n", w1_path, strerror(errno));
  		return -4;
 	}
 	if( read(fd, buf, sizeof(buf)) < 0 )
 	{
  		printf("read %s error : %s\n", w1_path, strerror(errno));
                return -5;
	}
 	ptr = strstr(buf, "t=");
 	if( !ptr )
 	{
  		printf("ERROR : Can not get temperature\n");
  		return -6;
 	}
 	
 	ptr+=2;
 	
 	*temp = atof(ptr)/1000.0;
 	
 	close(fd);
 	return 0;
}


int main(int argc, char **argv)
{
 	int    server_fd = -1;
 	int    rv = -1;
 	struct sockaddr_in servaddr;
 	char    *servip = "192.168.0.16";
 	int    port = 8889;
 	char    buf[1024];
 	int   ch = -1;
 	float   temp;
 	char    datime[32];
 	char   *user = "Linke";
 	int   connected = 0;
 	server_fd = socket(AF_INET, SOCK_STREAM, 0);
 	if(server_fd < 0)
 	{
  		printf("Create client socket unsuccessfully : %s\n", strerror(errno));
  		return -1;
 	}
 	printf("Create client socket[%d] successfully!\n", server_fd);
 	
 	memset(&servaddr, 0, sizeof(servaddr));
 	servaddr.sin_family = AF_INET;
 	servaddr.sin_port = htons(port);
 	inet_aton(servip, &servaddr.sin_addr);
 	
 	signal(SIGTERM, sig_handler);
 	signal(SIGALRM, sig_handler);
 	signal(SIGINT, sig_handler);
 	signal(SIGBUS, sig_code); 
 	signal(SIGILL, sig_code);
 	signal(SIGSEGV, sig_code);
 	while(!g_stop)
 	{
  		get_sys_time(datime);
  		ds18b20_get_temperature(&temp);
  		memset(buf, 0, sizeof(buf));
  		snprintf(buf,sizeof(buf),"%s/%s/%f",user,datime,temp);
  		
  		if( !connected )
  		{
   			rv = connect(server_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
   			if(rv < 0)
   			{
    				printf("Connect to server[%s:%d] unsuccessfully : %s\n", servip, port, strerror(errno));
   				sleep(5);
   			}
   			printf("Connect to server[%s:%d] successfully!\n", servip, port);
   		}
   	
  		rv = write(server_fd, buf, strlen(buf));
  		if(rv < 0)
  		{
  	 		printf("Write to server[%d] unsuccessfully : %s\n", server_fd, strerror(errno));
   			return -1;
   			break;
  		}
  		printf("Write to server[%d] successfully!\n", server_fd);

       	 	memset(buf, 0, sizeof(buf));
   		rv = read(server_fd, buf, sizeof(buf));
  		if(rv < 0)
  		{
   			connected = 1;
   			printf("Read data from server[%d] unsuccessfully : %s\n", server_fd, strerror(errno));
   			sleep(5);
   			continue;
  		}
  		else if(rv == 0)
  		{
   			printf("Server[%d] get disconnected!\n", server_fd);
   			return -1;
   			break;
  		}
  		else if(rv > 0)
  		{
   			connected = 1;
   			printf("Read %d bytes data : %s", rv, buf);
  		}
  		sleep(10);
   	}
   	
	close(server_fd);
 	return 0;
}

4.总结

(1)由于第一次独立完成,代码过于冗长,各个功能模块的实现以及代码思路还不够清晰。
(2)在编程过程中遇到很多问题,例如在开启服务器端后,由于安装信号,停止服务器端运行后,无法释放端口,后通过netstat -tlnp查看端口被占用,后kill -9加进程号即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值