一、进程
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#include #include #include #include #include #include #include #include #define MSG_STR "Hello yanpn" #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] successfuln",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port)); pid=fork(); if(pid<0) { printf("create child process failure:%sn",strerror(errno)); close(clifd); continue; } else if(0==pid) { char buf[1024]; close(listen_fd); printf("child process communicate with new clientn"); memset(buf,0,sizeof(buf)); rv=read(clifd,buf,sizeof(buf)); if(rv<0) { printf("Read data from client sockfd[%d] failure: %sn", clifd,strerror(errno)); close(clifd); exit(0); } else if(0==rv) { printf("Socket[%d] get disconnectedn", clifd); close(clifd); exit(0); } else if(rv>0) { printf("read %d bytes from client:%sn",rv,buf); } rv=write(clifd, MSG_STR, strlen(MSG_STR)); if(rv < 0) { printf("write to client by socket[%d] failure:%sn",listen_fd,strerror(errno)); close(clifd); exit(0); } sleep(1); printf("close client socket[%d] and child process exitn", 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 8900n", 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] successn",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:%sn",listenfd,listen_port,strerror(errno)); return -2; } printf("socket[%d] bind on port[%d] for ip address successn",listenfd,listen_port); listen(listenfd,BACKLOG); printf("start listen on port[%d]n",listen_port); return listenfd; }
客户端的代码
#include#include #include #include #include #include #include #include #include #include #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:%sn",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:%sn",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:%sn",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:%sn",strerror(errno)); return -3; } else if(rv==0) { printf("client connetc to server get disconnectn"); 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 12345n", program); return ; }
运行结果如下::



