Linux多进程编程实例
前言:编写多进程程序时,我们应该了解一下,创建一个子进程时,操作系统内核是怎样做的。当通过fork函数创建新的子进程时,内核将父进程的用户地址空间的内容复制给子进程,这样父子进程拥有各自独立的用户空间,当父进程修该变量的值时不会影响子进程中的相应变量。但为了提高效率,Linux采用了COW(copy on write)算法,子进程创建时,父子进程享有相同的地址空间,只是在页表中设置cow标识,只有在父进程或子进程执行写数据操作时,才为子进程申请一个物理页,将父进程空间中相应数据所在页的内容复制到该物理页,然后将该页映射到子进程用户地址空间的适当位置。此外,子进程还继承父进程的其他资源,例如父进程打开的文件描述符和工作目录等(因为子进程能继承父进程的文件描述符,所以实现进程之间通信时可以采用管道通信方式)。 子进程和父进程的代码区,以及初始数据是一模一样的,只是fork函数返回的pid在父子进程中有不同的值,所以根据pid的分支语句父子进程会执行不同的结果。如果我们只是想用子进程完成某一部分的功能,当功能完成后我们应该立即使用exit或者return函数结束子进程,不然,若子进程的创建是在一个循环中,并且没有使用exit或者return推出,那么子进程,会像父进程一样执行循环中的代码,然后子进程又创建子进程,无限循环,具体例子额看后面的一个服务器端代码。
进程创建与退出
相关api
pid_t fork() //#include <unistd.h> 创建进程
void exit(int status) //#include <stdlib.h> 退出进程,退出时会调用atexit注册的函数,先注册的后调用,exit函数还会按需调用fclose函数关闭打开的文件流
int atexit(void(*func)(void)) //#include <stdlib.h> 为进程注册退出时调用的函数
void _exit(int status) //#include <unistd.h> 直接退出进程
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int glob=;
static void my_exit1(void) //进程退出时调用函数
{
printf("pid=%d first exit handler\n",getpid());
} static void my_exit2(void)
{
printf("pid=%d second exit handler\n",getpid());
} int main()
{
int local;
pid_t pid;
local=;
if(atexit(my_exit1)!=) //为进程注册的退出时调用函数也会被子进程共享,先注册的后调用
{
perror("atexit");
} if(atexit(my_exit2)!=)
{
perror("atexit");
} if((pid=fork())==)
{
printf("child pid is %d\n",getpid()); //子进程执行某个任务完后尽量使用exit退出,不然,若父进程中创建的子进程位于循环中,可能会引起未知的行为
}
else if(pid>)
{
sleep();
glob++;
local--;
printf("father pid is %d\n",getpid());
}
else
{
perror("fork");
}
printf("pid=%d,glob=%d,localar=%d\n",getpid(),glob,local);//这段代码父子进程共享
return ;//也可以使用exit(0)
}
加载可执行文件映像
#include <unistd.h>
int execl(const char *path,const char *arg,...); // l表示命令行参数为以0结束的多个字符串组成 ,v表示命令行参数为以0结束的字符串数组组成
int execle(const char *path,const char *arg,...,char *const envp[]); //e表示指定环境表量,原来的环境变量不起作用
int execlp(const char *file,const char *arg,...); //p表示可执行映像文件在环境变量path路径中查找
int execv(cosnt char *path,char *const argv[]);
int execve(const char *path,char *const argv[],char *const envp[]);
int execvp(const char *file,char *const argv[]);
path 代表可执行文件路径,arg代表命令行参数
//testexec.c 被调用程序
#include <stdio.h>
int glob=;
extern char **environ; int main(int argc,char *argv[])
{
int local=;
int k;
char **ptr=environ;
glob++;
local++;
printf("&glob=%x,&local=%x\n",&glob,&local); //打印变量的地址
printf("argc=%d\n",argc);
for(k=;k<argc;++k)
{
printf("argv[%d]\t %s\n",k,argv[k]); //打印命令行参数
}
for(ptr=environ;*ptr!=;++ptr)
{
printf("%s\n",*ptr); //打印环境变量
}
return ;
}
//useexec.c
#include <stdio.h>
#include <unistd.h>
int main()
{
char *nenv[]={"NAME=value","NEXT=nextvale",(char*)};
char *nargv[]={"testexec","param1","param2",(char *)}; //命令行参数都以0结尾
pid_t pid;
pid=fork();
switch(pid)
{
case :
execve("./testexec",nargv,nenv); //指定环境变量,原来的环境变量不起作用
//execl("./testexec","testexec",0); //不指定环境表量
perror("exec");
exit();
case -:
perror("fork");
exit();
default:
wait(NULL);
printf("exec is completed\n");
exit();
}
}
等待子进程结束
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * status) //暂停执行,直到一个子进程结束,成功返回进程pid,否则返回-1
pid_t waitpid(pid_t pid,int *status,int options) //等待指定子进程结束,options指定等待方式
返回值:若设置了WNOHANG且未发现子进程则返回0,出错则返回-1
pid的意义
pid<-1 等待pid所代表的进程组中的进程
pid=-1 等待任何子进程
pid=0 等待与该进程同组的进程
pid>0 等待的进程标识
options的意义
WNOHANG //表示不阻塞
WUNTRACED //当有子进程结束时返回
//一个回声服务器服务端例子 tcpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#define PORT 4008
#define BACKLOG 10
#define BUFSIZE 4096
//void process(char * cmd);
int main(int argc,char* argv[])
{
int lsockfd,rsockfd;
struct sockaddr_in lsocket,rsocket;
if((lsockfd=socket(AF_INET,SOCK_STREAM,))<)
{
perror("socket");
exit();
} lsocket.sin_family=AF_INET;
lsocket.sin_port=htons(PORT);
lsocket.sin_addr.s_addr=INADDR_ANY;
bzero(&(lsocket.sin_zero),); if(bind(lsockfd,(struct sockaddr *)&lsocket,sizeof(struct sockaddr))<)
{
perror("bind");
exit();
} if(listen(lsockfd,BACKLOG)<)
{
perror("listen");
exit();
} int sin_size=sizeof(struct sockaddr);
int count=;
while()
{
printf("wait for connecting!\n");
if((rsockfd=accept(lsockfd,(struct sockaddr *)&rsocket,&sin_size))<)
{
perror("accept");
continue;
}
count++;
printf("someone connect!,current people %d\n",count); if(!fork())
{
char str[BUFSIZE];
int numbytes=;
while()
{
if((numbytes=recv(rsockfd,str,BUFSIZE-,))<)
{
perror("recv");
break;
}
str[numbytes]='\0';
if(strcmp(str,"quit")==)
{
printf("client quit!\n");
break;
} printf("receive a message: %s\n",str);
if(send(rsockfd,str,strlen(str),)<)
{
perror("send");
break;
}
}
close(rsockfd);
exit(); }
while(waitpid(-,NULL,WNOHANG)>) //此处不会阻塞若第三个参数为WUNTRACED则会阻塞
{
count--;
printf("someone quit!,current people have %d\n",count);
} }
return ;
}
Linux多进程编程实例的更多相关文章
- Linux 多进程编程实例(一)
文章目录 目标: main.c process1.c process2.c 目标: 一个进程,创建两个子进程,利用exec函数族使两个子进程执行不同的程序.子进程1执行ls -l命令后正常返回,子进程 ...
- PHP多进程编程实例
这篇文章主要介绍了PHP多进程编程实例,本文讲解的是在Linux下实现PHP多进程编程,需要的朋友可以参考下 羡慕火影忍者里鸣人的影分身么?没错,PHP程序是可以开动影分身的!想完成任务,又觉得一个进 ...
- 嵌入式linux多进程编程
嵌入式linux多进程编程 在主程序显示文本菜单.提供例如以下服务.要求每一个服务都通过生成子进程来提供. 服务包含:日历信息显示,日期信息显示,推断闰年服务,文件复制功能,数字排序功能.退出功能. ...
- linux内核模块编程实例
linux内核模块编程实例 学号:201400814125 班级:计科141 姓名:刘建伟 1.确定本机虚拟机中的Ubuntu下Linux的版本 通过使用命令uname -a/uname -r/una ...
- Linux c编程实例_例子
例一:字符与整型变量的实现 #include <stdio.h> int main() { int c1,c2; char c3; c1='a'-'A'; c2='b'-'B'; c3=; ...
- Linux多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- Linux网络编程实例解析
**************************************************************************************************** ...
- Linux多进程编程
进程的状态 Linux进程有7种基础状态(两种running算一种),除了traced都可以用$ps命令查看,$ps可以查看的进程状态如下,更多进程状态信息参见Linux Process VS Thr ...
- linux 定时器编程实例(完善中).....
最近在写linux 下的定时器编程实验,测试发现 usleep函数在 x86 架构下的定时还是比较准确的,在arm9下 就不太准了. 今天用linux 下的setitimer()函数进行了定时 器的测 ...
随机推荐
- 数据模型(LP32 ILP32 LP64 LLP64 ILP64 )
数据模型(LP32 ILP32 LP64 LLP64 ILP64 ) 32位环境涉及"ILP32"数据模型,是因为C数据类型为32位的int.long.指针.而64位环境使用不同的 ...
- 1053: [HAOI2007]反素数ant
1053: [HAOI2007]反素数ant Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3480 Solved: 2036[Submit][St ...
- 【20171106早】BeEF 工具初探
老黑今天接触BeEF工具,首先要了解这个工具能够做什么? 0x01:功能介绍 专业文档:点击这里 通俗的说就是可以控制别的浏览器,获取浏览器的信息.然后做something 专业的说就是好用的渗透测试 ...
- JavaUtil_04_验证码生成器
一.原理 验证码其实就是随机串.原理上可分为两种: 1.简单的验证码 直接通过字母和数字的ASCII码生成.本文采用的验证码就是这种. 2.复杂的验证码 通过一个随机串,一个指定串(如accesske ...
- Android 开发笔记___RadioButton
horizontal <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" and ...
- Excel 生成SQL
'"&A21&"' Excel 中要做字符串连接 "& + 单元格地址 + &", 如果单纯做测试在某个单元格中测试输出内容 ...
- 微信小程序异步处理
直接看问题: 然后看打印的结果: 根据上面两图可以看出,代码上先执行的网络请求,再执行打印的变量,但是从下面打印的结果来看,先出结果的是执行打印变量的函数(aafn函数),再打印出网络请求succes ...
- 第三方软件内嵌IE出现纵向滚动条消失的BUG,奇葩的IE BUG 真是无奇不有
混了这么久 竟然还有这样难以解决的BUG,最后都跑到英文的MSDN上提问了,因为谷歌都谷不出朕的忧伤了,有木有. 提问原文如下:https://social.msdn.microsoft.com/Fo ...
- C#Session丢失问题的解决办法
关于c# SESSION丢失问题解决办法 我们在用C#开发程序的时候经常会遇到Session很不稳定,老是数据丢失.下面就是Session数据丢失的解决办法希望对您有好处.1.在WEB.CONFI ...
- JDBC数据库编程
常识名词:ODBC ,JDBC,JDBC API ,JDBC Driver API 数据准备,续上节: JDBC编程流程 最基本的JDBC操作 本段内容主要完成JDBC的增删查改操作 packa ...