一、进程 1.进程是动态的,是程序的一次运行活动。
2.进程在内存中由数据段、堆栈段和代码段组成。c程序内存分布图如下 3.每个进程都有自己的进程标识符pid,可以调用getpid()来得到自己的pid,getppid()获得父进程的标识符
4.创建子进程: pid_t fork(void); fork函数调用一次返回两次,子进程的返回值是0,父进程的返回值是子进程的PID,因此通常利用这个区别去设置判断语句令父子进程执行不同的操作。一般来说,fork之后的父子进程的执行顺序是不确定的,这个取决于调度算法。 fork之后的处理方法:
- fork之后子进程和父进程处理不同的代码段
- fork之后调用exec,让子进程处理完全不同的程序
pid_t vfork(void);
vfork创建一个子进程,但是该子进程并不对父进程地址空间进行拷贝而是直接共享。因此如果让子进程对父进程数据进行读或写都可能产生段错误。主要原因在于,因为vfork产生的子进程是要exec一个新程序的,于是也就不会引用该地址空间了,不过子进程再调用exec()或 exit()之前,他将在父进程的空间中运行,但如果子进程想尝试修改数据域(数据段、堆、栈)都会带来未知的结果,就算写实复制copyonwrite(COW)( 这些数据区域由父子进程共享,内核将他们的访问权限改成只读,如果父进程和子进程中的任何一个试图修改这些区域的时候,内核再为修改区域的那块内存制作一个副本。)机制也不如索性不复制节约时间。
vfork保证子进程先运行,在它调用exec或者exit后父进程才可以被调用执
5.wait()和waitpid() pid_t wait(int *status); 当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。因为子进程退出是一个异步事件,所以这种信号也是内核向父进程发送的一个异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态。进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。 参数: 参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针止。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,可以直接传NULL。 返回值: 如果成功,wait会返回被收集的子进程的进程ID 如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
pid_t waitpid(pid_t pid,int *status,int options); 系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,提供了另一种更灵活的方式。 二、使用多进程编程改写服务器的流程如下:
**服务器端的代码如下:**
#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 yanp\n"
#define BACKLOG 13
void printf_usage(char *program);
int socket_init(char *listen_ip,int listen_port);
int main(int argc,char **argv)
{
int daemon_run=0;
char *program;
int serv_port=0;
int listen_fd;
int clifd=-1;
int rv=-2;
int opt;
struct sockaddr_in cliaddr;
socklen_t cliaddr_len;
pid_t pid;
struct option long_options[] =
{
{"daemon", no_argument, NULL, 'b'},
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
program=argv[0];
while((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
{
switch(opt)
{
case 'b':
daemon_run=1;
break;
case 'p':
serv_port = atoi(optarg);
break;
case 'h':
printf_usage(program);
break;
default:
break;
}
}
if(!serv_port)
{
printf_usage(argv[0]);
return -1;
}
if((listen_fd=socket_init(NULL,serv_port))<0)
{
printf("socket_init failure error:%s",strerror(errno));
return -2;
}
if(daemon_run)
{
daemon(0,0);
}
while(1)
{
printf("start accept new client income...\n");
clifd=accept(listen_fd,(struct sockaddr *)&cliaddr,&cliaddr_len);
if(clifd<0)
{
printf("accept new client failure:%s",strerror(errno));
continue;
}
printf("accept new client[%s:%d] successful\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
pid=fork();
if(pid<0)
{
printf("create child process failure:%s\n",strerror(errno));
close(clifd);
continue;
}
else if(0==pid)
{
char buf[1024];
close(listen_fd);
printf("child process communicate with new client\n");
memset(buf,0,sizeof(buf));
rv=read(clifd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from client sockfd[%d] failure: %s\n", clifd,strerror(errno));
close(clifd);
exit(0);
}
else if(0==rv)
{
printf("Socket[%d] get disconnected\n", clifd);
close(clifd);
exit(0);
}
else if(rv>0)
{
printf("read %d bytes from client:%s\n",rv,buf);
}
rv=write(clifd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("write to client by socket[%d] failure:%s\n",listen_fd,strerror(errno));
close(clifd);
exit(0);
}
sleep(1);
printf("close client socket[%d] and child process exit\n", clifd);
close(clifd);
exit(0);
}
else if(pid>0)
{
close(clifd);
continue;
}
}
}
void printf_usage(char *program)
{
printf("使用方法:%s【选项】 \n", program);
printf(" %s是一个服务器程序,用来等待客户端的连接\n",program);
printf("\n传入参数\n");
printf(" -b[daemon]设置程序在后台运行\n");
printf(" -p[port ] 指定连接的端口号\n");
printf(" -h[help ] 打印帮助信息\n");
printf("\n例如: %s -b -p 8900\n", program);
return;
}
int socket_init(char *listen_ip,int listen_port)
{
int listenfd;
struct sockaddr_in servaddr;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("socket_server to create a TCP socket fd failure:[%s]\n",strerror(errno));
return -1;
}
printf("create a tcp socket fd[%d] success\n",listenfd);
int on=1;
if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
printf("setsockopt failure:%s",strerror(errno));
return -2;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(listen_port);
if(!listen_ip)
{
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
}
else
{
servaddr.sin_addr.s_addr=htonl(listen_port);
}
if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
{
printf("socket[%d] bind on port[%d] for ip address failure:%s\n",listenfd,listen_port,strerror(errno));
return -2;
}
printf("socket[%d] bind on port[%d] for ip address success\n",listenfd,listen_port);
listen(listenfd,BACKLOG);
printf("start listen on port[%d]\n",listen_port);
return listenfd;
}
客户端的代码
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#define MSG_STR "Hello yanp, Unix Network Program World!"
void print_usage(char *progname);
int main(int argc,char **argv)
{
int conn_fd = -1;
int rv = -1;
char buf[1024];
struct sockaddr_in serv_addr;
int serv_port;
char *serv_ip=NULL;
char *program=NULL;
int opt;
struct option long_options[] =
{
{"ip",required_argument,NULL,'i'},
{"port",required_argument, NULL, 'p'},
{"help",no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
program=argv[0];
while ((opt = getopt_long(argc, argv, "i:p:h", long_options, NULL)) != -1)
{
switch (opt)
{
case 'i':
serv_ip=optarg;
break;
case 'p':
serv_port = atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
break;
}
}
if( !serv_port ||!serv_ip)
{
print_usage(argv[0]);
return -1;
}
conn_fd=socket(AF_INET,SOCK_STREAM,0);
if(conn_fd<0)
{
printf("create client socket failure:%s\n",strerror(errno));
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(serv_port);
inet_aton(serv_ip,&serv_addr.sin_addr);
if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
{
printf("client[%d] connect to server[%s:%d] failure:%s\n",conn_fd,serv_ip,serv_port,strerror(errno));
return -1;
}
if(write(conn_fd,MSG_STR,strlen(MSG_STR))<0)
{
printf("write data to server[%s,%d] failure:%s\n",serv_ip,serv_port,strerror(errno));
return -2;
}
memset(buf,0,sizeof(buf));
rv=read(conn_fd,buf,sizeof(buf));
if(rv<0)
{
printf("read data from server failure:%s\n",strerror(errno));
return -3;
}
else if(rv==0)
{
printf("client connetc to server get disconnect\n");
return -4;
}
printf("read %d bytes from server:'%s'\n",rv,buf);
return 0;
}
void print_usage(char *program)
{
printf("用法:%s选项 \n", program);
printf(" %s 是一个客户端程序,需要指定连接服务器的ip地址和端口号\n",program);
printf(" -i[ip ] 指定需要连接服务器的ip地址\n");
printf(" -p[port ] 指定需要连接服务器的端口号\n");
printf(" -h[help ] 打印帮助信息\n");
printf("\n举例 %s -i 192.168.1.2 -p 12345\n", program);
return ;
}
运行结果如下::
|