c语言 多进程和多线程

c语言  多进程和多线程
c语言  多进程和多线程

一.多进程程序的特点

进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。

进程是资源管理的最小单位,线程是程序执行的最小单位。进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文切换开销。

进程的状态系统为了充分的利用资源,对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态.

新建表示进程正在被创建,

运行是进程正在运行,

阻塞是进程正在等待某一个事件发生,

就绪是表示系统正在等待CPU来执行命令,

完成表示进程已经结束了系统正在回收资源.

由于UNIX系统是分时多用户系统, CPU按时间片分配给各个用户使用,而在实质上应该说CPU按时间片分配给各个进程使用, 每个进程都有自己的运行环境以使得在CPU做进程切换时不会"忘记"该进程已计算了一半的"半成品”. 以DOS的概念来说, 进程的切换都是一次"DOS中断"处理过程, 包括三个层次:

1)用户数据的保存: 包括正文段(TEXT), 数据段(DATA,BSS), 栈段(STACK), 共享内

存段(SHARED MEMORY)的保存.

2)寄存器数据的保存: 包括PC(program counter,指向下一条要执行的指令的地址),

PSW(processor status word,处理机状态字), SP(stack pointer,栈指针), PCBP(pointer of

process control block,进程控制块指针), FP(frame pointer,指向栈中一个函数的local

变量的首地址), AP(augument pointer,指向栈中函数调用的实参位置), ISP(interrupt

stack pointer,中断栈指针), 以及其他的通用寄存器等.

3)系统层次的保存:

包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时

间片时能正常运行。既然系统已经处理好所有这些中断处理的过程, 我们做程序

还有什么要担心的呢? 我们尽可以使用系统提供的多进程的特点, 让几个程序精

诚合作, 简单而又高效地把结果给它搞出来。

另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程?将会对UNIX系统机制有一个较深的认识.首先我介绍一下多进程程序的一些突出的特点:

1.1并行化

一件复杂的事件是可以分解成若干个简单事件来解决的, 这在程序员的大脑中早就形成了这种概念, 首先将问题分解成一个个小问题, 将小问题再细分, 最后在一个合适的规模上做成一个函数. 在软件工程中也是这么说的. 如果我们以图的方式来思考, 一些小问题的计算是可以互不干扰的, 可以同时处理, 而在关键点则需要统一在一个地方来处理, 这样程序的运行就是并行的, 至少从人的时间观念上来说是这样的. 而每个小问题的计算又是较简单的.

1.2简单有序

这样的程序对程序员来说不亚于管理一班人, 程序员为每个进程设计好相应的功能, 并通过一定的通讯机制将它们有机地结合在一起, 对每个进程的设计是简单的, 只在总控部分小心应付(其实也是蛮简单的), 就可完成整个程序的施工.

1.3.互不干扰

这个特点是操作系统的特点, 各个进程是独立的, 不会串位.

1.4.事务化

比如在一个数据电话查询系统中, 将程序设计成一个进程只处理一次查询即可, 即完成一个事务. 当电话查询开始时, 产生这样一个进程对付这次查询; 另一个电话进来时, 主控程序又产生一个这样的进程对付, 每个进程完成查询任务后消失. 这样的编程多简单, 只要做一次查询的程序就可以了.

二.常用的多进程编程的系统调用

2.1.fork() 创建一个新的进程.

功能:创建一个新的进程.

语法:

#include

#include

pid_t fork();

说明:本系统调用产生一个新的进程, 叫子进程, 是调用进程的一个复制品. 调用进程叫父进程, 子进程继承了父进程的几乎所有的属性。

进程:代码段(程序代码)

堆栈段(局部变量、函数返回地址、函数参数)

数据段(全局变量、常数等)

在Linux系统中,系统调用fork后,内核为完成系统调用fork要进行几步操作:

第一步,为新进程在进程表中分配一个表项。系统对一个普通用户可以同时运行的进程数是有限制的,对超级用户没有该限制,但不能超过进程表的最大表项的

数目。

第二步,给子进程一个唯一的进程标识号(PID)。该进程标识号其实就是该表项在进程表中的索引号。

第三步,复制一个父进程的进程表项的副本给子进程。内核初始化子进程的进程表项时,是从父进程处拷贝的。所以子进程拥有与父进程一样的uid、当前目录、

当前根、用户文件描述符表等。

第四步,把与父进程相连的文件表和索引节点表的引用数加1。这些文件自动地与该子进程相连。

第五步,内核为子进程创建用户级上下文。内核为子进程的代码段分配内存,并复制父进程的区内容,生成的是进程的静态部分。

第六步,生成进程的动态部分,然后对父进程返回子进程的pid,对子进程返回0。

从父进程拷贝的内容主要有:

●用户标识符,包括实际用户号(real)和有效用户号(effective);

●环境变量

●打开的文件描述符、套接字描述符

●信号处理设置

●堆栈

●目录

●进程组标志(process ID)

●会晤组标志(session ID)

●正文

子进程特有内容:

●进程号

●父进程号

●进程执行时间

●未处理的信号被处理为空

●不继承异步的输入输出操作

简述:fork() 调用成功时,分别返回两个整数,对父进程返回〉0的整数,对子进程返回0,

函数执行过程:

①内核在系统进程表中,创建一个新条目;

②复制父进程内容(已打开的文件描述符、堆栈、正文等);

③修改两者的堆栈,给父进程返回子进程号,给子进程返回0(父进程知道每个子进程

的标志号,而子进程可根据需要调用getppid() 来获得父进程的标志号)。

例子:

pid_t fork(void)

#include

pid_t pid;

{

//子进程代码

exit(0);

}

else if(pid>0)

{

//父进程代码

exit(0);

}

else

{

printf("Error");

exit(1);

}

2.2.system() 子进程执行指定的命令

功能:产生一个新的进程, 子进程执行指定的命令.

语法:

#include

#include

int system(string)

char *string;

说明:

本调用将参数string传递给一个命令解释器(一般为sh)执行, 即string被解释为一条命令, 由sh执行该命令.若参数string为一个空指针则为检查命令解释器是否存在.

该命令可以同命令行命令相同形式, 但由于命令做为一个参数放在系统调用中, 应注意编译时对特殊意义字符的处理. 命令的查找是按PATH环境变量的定义的. 命令所生成的后果一般不会对父进程造成影响.

返回值:当参数为空指针时, 只有当命令解释器有效时返回值为非零. 若参数不为空指针, 返回值为该命令的返回状态(同waitpid())的返回值. 命令无效或语法错误则返回非零值,所执行的命令被终止. 其他情况则返回-1.

例子:char command[81];

int i;

for (i=1;i<8;i++) {

sprintf(command,"ps t tty%02i",i);

system(command);

}

2.3.exec() 执行一个文件

功能:执行一个文件

语法

#include

int execve(const char* path, char* const* argv,char* const* envp);

int execl(const char* path, char* arg,...);

int execp(const char* file, char* arg,...);

int execle(const char* path, const char* argv,...,char* const* envp); int execv(const char* path, char* const* arg);

int execvp(const char* file, char* const* arg);

说明:

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件

其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

fork()和exec()这两个函数,前者用于并行执行,父、子进程执行相同正文中的不同部分;后者用于调用其他进程,父、子进程执行不同的正文,调用前,一般应为子进程创造一个干净的环境。

fork()以后,父、子进程共享代码段,并只重新创建数据有改变的页(段页式管理)exec()以后,建立新的代码段,用被调用程序的内容填充。

前者的子进程执行后续的公共代码,后者的子进程不执行后续的公共代码。

父、子进程以及各个子进程执行的顺序不定。

.

例子:printf("now this process will be ps command\n");

execl("/bin/ps","ps","-ef",NULL);

2.4.popen() 初始化从/到一个进程的管道

功能:初始化从/到一个进程的管道.

语法:

#include

FILE *popen(command,type)

char *command,type;

说明:本系统调用在调用进程和被执行命令间创建一个管道.

参数command做为被执行的命令行.type做为I/O模式,"r"为从被

执行命令读,"w"为向被执行命令写.返回一个标准流指针,做为管

道描述符,向被执行命令读或写数据(做为被执行命令的STDIN或STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令

的输出信息或者向命令输入信息.

返回值:不成功则返回NULL,成功则返回管道的文件指针.

2.5.pclose() 关闭到一个进程的管道

功能:关闭到一个进程的管道.

语法:

#include

int pclose(strm)

FILE *strm;

说明:本系统调用用于关闭由popen()打开的管道,并会等待由popen()

激活的命令执行结束后,关闭管道后读取命令返回码.

返回值:若关闭的文件描述符不是由popen()打开的,则返回-1.

例子:printf("now this process will call popen system call\n");

FILE * fd;

if ((fd=popen("ps -ef","r"))==NULL) {

printf("call popen failed\n");

return;

}

else {

char str[80];

while (fgets(str,80,fd)!=NULL)

printf("%s\n",str);

}

pclose(fd);

2.6.wait() 等待一个子进程返回并修改状态

功能:等待一个子进程返回并修改状态

语法:

#include

#include

pid_t wait(stat_loc)

int *stat_loc;

说明:允许调用进程取得子进程的状态信息.调用进程将会挂起直到其

一个子进程终止.

返回值:等待到一个子进程返回时,返回值为该子进程号,否则返回值为

-1.同时stat_loc返回子进程的返回值.

例子:/*父进程*/

if (fork()>0) {

wait((int *)0);

/*父进程等待子进程的返回*/

}

else {

/*子进程处理过程*/

exit(0);

}

2.7.waitpid() 等待指定进程号的子进程的返回并修改状态

功能:等待指定进程号的子进程的返回并修改状态

语法:

#include

#include

pid_t waitpid(pid,stat_loc,options)

pid_t pid;

int *stat_loc,options;

说明:当pid等于-1,options等于0时,该系统调用等同于wait().否则该

系统调用的行为由参数pid和options决定.

pid指定了一组父进程要求知道其状态的子进程:

-1:要求知道任何一个子进程的返回状态.

>0:要求知道进程号为pid值的子进程的状态.

<-1:要求知道进程组号为pid的绝对值的子进程的状态.

options参数为以比特方式表示的标志以或运算组成的位图,每个

标志以字节中某个比特置1表示:

WUNTRACED:报告任何未知而又已停止运行的指定进程号的子进

程的状态.该子进程的状态自停止运行时起就没有被报告过.

WCONTINUED:报告任何继续运行的指定进程号的子进程的状态,

该子进程的状态自继续运行起就没有被报告过.

WHOHANG:若调用本系统调用时,指定进程号的子进程的状态目

前并不是立即有效的(即可被立即读取的),调用进程并被暂停执行.

WNOW AIT:保持将其状态设置在stat_loc的进程在可等待状态.

该进程将等待直到下次被要求其返回状态值.

返回值:等待到一个子进程返回时,返回值为该子进程号,否则返回值为 1.

同时stat_loc返回子进程的返回值.

例子:pid_t pid;

int stat_loc; /*父进程*/

if ((pid="fork())">0) {

waitpid(pid,&stat_loc,0);

/*父进程等待进程号为pid的子进程的返回*/

}

else {

/*子进程的处理过程*/

exit(1);

}

/*父进程*/

printf("stat_loc is [%d]\n",stat_loc);

/*字符串"stat_loc is [1]"将被打印出来*/

2.8.setpgrp() 设置进程组号和会话号

功能:设置进程组号和会话号.

语法:

#include

pid_t setpgrp()

说明:若调用进程不是会话首进程.将进程组号和会话号都设置为与它的进程号相等.并释放调用进程的控制终端.

返回值:调用成功后,返回新的进程组号.

例子:/*父进程处理*/

if (fork()>0) {

/*父进程处理*/

}

else {

setpgrp();

/*子进程的进程组号已修改成与它的进程号相同*/

exit(0);

}

2.9.exit() 终止进程

功能:终止进程.

语法:

#include

void exit(status)

int status;

说明:调用进程被该系统调用终止.引起附加的处理在进程被终止前全部结束.

返回值:无

2.10.signal() 信号管理功能

功能:信号管理功能

语法:

#include

void (*signal(sig,disp))(int)

int sig;

void (*disp)(int);

void (*sigset(sig,disp))(int)

int sig;

void (*disp)(int);

int sighold(sig)

int sig;

int sigrelse(sig)

int sig;

int sigignore(sig)

int sig;

int sigpause(sig)

int sig;

说明:这些系统调用提供了应用程序对指定信号的简单的信号处理. signal()和sigset()用于修改信号定位.参数sig指定信号(除了

SIGKILL和SIGSTOP,这两种信号由系统处理,用户程序不能捕捉到). disp指定新的信号定位,即新的信号处理函数指针.可以为

SIG_IGN,SIG_DFL或信号句柄地址.

若使用signal(),disp是信号句柄地址,sig不能为SIGILL,SIGTRAP

或SIGPWR,收到该信号时,系统首先将重置sig的信号句柄为SIG_DFL, 然后执行信号句柄.

若使用sigset(),disp是信号句柄地址,该信号时,系统首先将该

信号加入调用进程的信号掩码中,然后执行信号句柄.当信号句柄

运行结束

后,系统将恢复调用进程的信号掩码为信号收到前的状态.另外,

使用sigset()时,disp为SIG_HOLD,则该信号将会加入调用进程的

信号掩码中而信号的定位不变.

sighold()将信号加入调用进程的信号掩码中.

sigrelse()将信号从调用进程的信号掩码中删除.

sigignore()将信号的定位设置为SIG_IGN.

sigpause()将信号从调用进程的信号掩码中删除,同时挂起调用

进程直到收到信号.

若信号SIGCHLD的信号定位为SIG_IGN,则调用进程的子进程在终

止时不会变成僵死进程.调用进程也不用等待子进程返回并做相

应处理.

返回值:调用成功则signal()返回最近调用signal()设置的disp的值.

否则返回SIG_ERR.

例子一:设置用户自己的信号中断处理函数,以SIGINT信号为例:

int flag=0;

void myself()

{

flag=1;

printf("get signal SIGINT\n");

/*若要重新设置SIGINT信号中断处理函数为本函数则执行以

*下步骤*/

void (*a)();

a=myself;

signal(SIGINT,a);

flag=2;

}

main()

{

while (1) {

sleep(2000); /*等待中断信号*/

if (flag==1) {

printf("skip system call sleep\n");

exit(0);

}

if (flag==2) {

printf("skip system call sleep\n");

printf("waiting for next signal\n");

}

}

}

2.11.kill() 向一个或一组进程发送一个信号

功能:向一个或一组进程发送一个信号.

语法:

#include

#include

int kill(pid,sig);

pid_t pid;

int sig;

说明:本系统调用向一个或一组进程发送一个信号,该信号由参数sig指定,为系统给出的信号表中的一个.若为0(空信号)则检查错误但

实际上并没有发送信号,用于检查pid的有效性.

pid指定将要被发送信号的进程或进程组.pid若大于0,则信号将

被发送到进程号等于pid的进程;若pid等于0则信号将被发送到所

有的与发送信号进程同在一个进程组的进程(系统的特殊进程除

外);若pid小于-1,则信号将被发送到所有进程组号与pid绝对值

相同的进程;若pid等于-1,则信号将被发送到所有的进程(特殊系

统进程除外).

信号要发送到指定的进程,首先调用进程必须有对该进程发送信

号的权限.若调用进程有合适的优先级则具备有权限.若调用进程

的实际或有效的UID等于接收信号的进程的实际UID或用setuid()

系统调用设置的UID,或sig等于SIGCONT同时收发双方进程的会话号相同,则调用进程也有发送信号的权限.

若进程有发送信号到pid指定的任何一个进程的权限则调用成功,

否则调用失败,没有信号发出.

返回值:调用成功则返回0,否则返回-1.

例子:假设前一个例子进程号为324,现向它发一个SIGINT信号,让它做信号处理:

kill((pid_t)324,SIGINT);

2.12.alarm() 设置一个进程的超时时钟

功能:设置一个进程的超时时钟.

语法:

#include

unsigned int alarm(sec)

unsigned int sec;

说明:指示调用进程的超时时钟在指定的时间后向调用进程发送一个SIGALRM信号.设置超时时钟时时间值不会被放入堆栈中,后一次

设置会把前一次(还未到超时时间)冲掉.

若sec为0,则取消任何以前设置的超时时钟.

fork()会将新进程的超时时钟初始化为0.而当一个进程用exec()

族系统调用新的执行文件时,调用前设置的超时时钟在调用后仍

有效.

返回值:返回上次设置超时时钟后到调用时还剩余的时间秒数.

例子:int flag=0;

void myself()

{

flag=1;

printf("get signal SIGALRM\n");

/*若要重新设置SIGALRM信号中断处理函数为本函数则执行

*以下步骤*/

void (*a)();

a=myself;

signal(SIGALRM,a);

flag=2;

}

main()

{

alarm(100); /*100秒后发超时中断信号*/

while (1) {

sleep(2000); /*等待中断信号*/

if (flag==1) {

printf("skip system call sleep\n");

exit(0);

}

if (flag==2) {

printf("skip system call sleep\n");

printf("waiting for next signal\n");

}

}

}

2.1

3.msgsnd() 发送消息到指定的消息队列中

功能:发送消息到指定的消息队列中.

语法:

#include

#include

#include

int msgsnd(msqid,msgp,msgsz,msgflg)

int msqid;

void *msgp;

size_t msgsz;

int msgflg;

说明:发送一个消息到由msqid指定消息队列标识号的消息队列.

参数msgp指向一个用户定义的缓冲区,并且缓冲区的第一个域应

为长整型,指定消息类型,其他数据放在缓冲区的消息中其他正文

区内.下面是消息元素定义:

long mtype;

char mtext[];

mtype是一个整数,用于接收进程选择消息类型.

mtext是一个长度为msgsz字节的任何正文,参数msgsz可从0到系

统允许的最大值间变化.

msgflg指定操作行为:

. 若(msgflg&IPC_NOW AIT)是真的,消息并不是被立即发送而调用

进程会立即返回.

. 若(msgflg&IPC_NOW AIT)不是真的,则调用进程会被挂起直到下

面情况之一发生:

* 消息被发送出去.

* 消息队列标志被系统删除.系统调用返回-1.

* 调用进程接收到一个未被忽略的中断信号,调用进程继续

执行或被终止.

调用成功后,对应指定的消息队列的相关结构做如下动作:

. 消息数(msg_qnum)加1.

. 消息队列最近发送进程号(msg_lspid)改为调用进程号.

. 消息队列发送时间(msg_stime)改为当前系统时间.

以上信息可用命令ipcs -a看到.

返回值:成功则返回0,否则返回-1.

2.14.msgrcv() 从消息队列中取得指定类型的消息

功能:从消息队列中取得指定类型的消息.

语法:

#include

#include

#include

int msgrcv(msqid,msgp,msgsz,msgtyp,msgflg)

int msqid;

void *msgp;

int msgsz;

long msgtyp;

int msgflg;

说明:本系统调用从由msqid指定的消息队列中读取一个由msgtyp指定

类型的消息到由msgp指向的缓冲区中,同样的,该缓冲区的结构如

前所述,包括消息类型和消息正文.msgsz为可接收的消息正文的

字节数.若接收到的消息正文的长度大于msgsz,则会被截短到

msgsz字节为止(当消息标志msgflg&MSG_NOERROR为真时),截掉的

部份将被丢失,而且不通知消息发送进程.

msgtyp指定消息类型:

. 为0则接收消息队列中第一个消息.

. 大于0则接收消息队列中第一个类型为msgtyp的消息.

. 小于0则接收消息队列中第一个类型值不小于msgtyp绝对值且

类型值又最小的消息.

msgflg指定操作行为:

. 若(msgflg&IPC_NOW AIT)是真的,调用进程会立即返回,若没有

接收到消息则返回值为-1,errno设置为ENOMSG.

. 若(msgflg&IPC_NOW AIT)不是真的,则调用进程会被挂起直到下

面情况之一发生:

* 队列中的消息的类型是有效的.

* 消息队列标志被系统删除.系统调用返回-1.

* 调用进程接收到一个未被忽略的中断信号,调用进程继续

执行或被终止.

调用成功后,对应指定的消息队列的相关结构做如下动作:

. 消息数(msg_qnum)减1.

. 消息队列最近接收进程号(msg_lrpid)改为调用进程号.

. 消息队列接收时间(msg_rtime)改为当前系统时间.

以上信息可用命令ipcs -a看到.

返回值:调用成功则返回值等于接收到实际消息正文的字节数.

不成功则返回-1.

2.15.msgctl() 消息控制操作

功能:消息控制操作

语法:

#include

#include

#include

int msgctl(msqid,cmd,buf)

int msqid,cmd;

struct msqid_ds *buf;

说明:本系统调用提供一系列消息控制操作,操作动作由cmd定义,以下cmd定义值表明了各操作动作的定义.

. IPC_STAT:将msqid相关的数据结构中各个元素的当前值放入由

buf指向的结构中.

. IPC_SET:将msqid相关的数据结构中的下列元素设置为由buf指

向的结构中的对应值.

msg_perm.uid

msg_perm.gid

msg_perm.mode

msg_qbytes

本命令只能由有效UID等于msg_perm.cuid或msg_perm.uid的

进程或有效UID有合适权限的进程操作.只有具有合适权限的

用户才能增加msg_qbytes的值.

. IPC_RMID:删除由msqid指示的消息队列.将它从系统中删除并

破坏相关的数据结构.

本命令只能由有效UID等于msg_perm.cuid或msg_perm.uid的

进程或有效UID有合适权限的进程操作.

返回值:调用成功则返回值为0,否则为-1.

2.16.msgget() 取得一个消息队列

功能:取得一个消息队列.

语法:

#include

#include

#include

int msgget(key,msgflg)

key_t key;

int msgflg;

说明:本系统调用返回与参数key相关的消息队列的标识符.

若以下事实成立,则与消息队列相关的标识符和数据结构将被创

建出来:

. 若参数key等于IPC_PRIV ATE.

. 若参数key没有一个已存在的消息队列标识符与之相关,同时值(msgflg&IPC_CREAT)为真.

创建消息队列时,与新的消息队列标识符相关的数据结构将被初

始化为如下:

. msg_perm.cuid和msg_perm.uid设置为调用进程的有效UID.

. msg_perm.cgid和msg_perm.gid设置为调用进程的有效GID.

. msg_perm.mode访问权限比特位设置为msgflg访问权限比特位.

. msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime设置为0.

. msg_ctime设置为当前系统时间.

. msg_qbytes设置为系统允许的最大值.

返回值:调用成功则返回一非0值,称为消息队列标识符;否则返回值为-1. 例子:本例将包括上述所有消息队列操作的系统调用:

#define RKEY 0x9001L /*读消息队列的KEY值*/

#define WKEY 0x9002L /*写消息队列的KEY值*/

#define MSGFLG 0666 /*消息队列访问权限*/

#define IPC_W AIT 0 /*等待方式在include文件中未定义*/

int rmsqid; /*读消息队列标识符*/

int wmsqid; /*写消息队列标识符*/

struct msgbuf {

long mtype;

char mtext[200];

} buf;

/*若读消息队列已存在就取得标识符,否则则创建并取得标识符*/

if ((rmsqid=msgget(RKEY,MSGFLG|IPC_CREAT))<0) {

printf("get read message queue failed\n");

exit(1);

} /*若写消息队列已存在则失败,若不存在则创建并取得标识符*/

if ((wmsqid="msgget(WKEY," MSGFLG|IPC_CREAT|IPC_TRUNC))<0) { printf("get write message queue failed\n");

exit(2);

} /*接收所有类型的消息*/

if (msgrcv(rmsqid,&buf,sizeof(struct msgbuf)-sizeof(long), 0L,IPC_W AIT)>0) printf("get %ld type message from queue:%s\n",

buf.mtype,buf.mtext);

else {

printf("get message failed\n");

exit(3);

}

buf.mtype=3L;

if (msgsnd(wmsqid,&buf,sizeof(struct msgbuf)-sizeof(long),

IPC_NOW AIT)>0)

printf("send message OK\n");

else {

printf("send message failed\n");

exit(4);

}

msgctl(wmsqid,IPC_RMID,(struct msqid *)NULL);

2.17.shmat() 联接共享内存的操作

功能:联接共享内存的操作.

语法:

#include

#include

#include

void *shmat(shmid,shmaddr,shmflg)

int shmid;

void *shmaddr;

int shmid;

说明:将由shmid指示的共享内存联接到调用进程的数据段中.被联接的段放在地址,该地址由以下准则指定:

. 若shmaddr等于(void *)0,则段联接到由系统选择的第一个可

用的地址上.

. 若shmaddr不等于(void *)0同时(shmflg&SHM_RND)值为真,则

段联接到由(shmaddr-(shmaddr%SHMLBA))给出的地址上.

. 若shmaddr不等于(void *)0同时(shmflg&SHM_RND)值为假,则

段联接到由shmaddr指定的地址上.

若(shmflg&sSHM_RDONLY)为真并且调用进程有读允许,则被联接

的段为只读;否则,若值不为真且调用进程有读写权限,则被联接

的段为可读写的.

返回值:若调用成功则返回被联接的共享内存段在数据段上的启始地址. 否则返回值为-1.

2.18.shmdt() 断开共享内存联接的操作

功能:断开共享内存联接的操作.

语法:

#include

#include

#include

void *shmdt(shmaddr)

void *shmaddr;

说明:本系统调用将由shmaddr指定的共享内存段从调用进程的数据段脱离出去.

返回值:若调用成功则返回值为0,否则返回值为-1.

2.19.shmget() 取得共享内存段

功能:取得共享内存段

语法:

#include

#include

#include

int shmget(key,size,shmflg)

key_t key;

int size,shmflg;

说明:本系统调用返回key相关的共享内存标识符.

共享内存标识符和相关数据结构及至少size字节的共享内存段能

正常创建,要求以下事实成立:

. 参数key等于IPC_PRIV ATE.

. 参数key没有相关的共享内存标识符,同时(shmflg&IPC_CREAT)

值为真.

共享内存创建时,新生成的共享内存标识相关的数据结构被初始

化如下:

. shm_perm.cuid和shm_perm.uid设置为调用进程的有效UID.

. shm_perm.cgid和shm_perm.gid设置为调用进程的有效GID.

. shm_perm.mode访问权限比特位设置为shmflg访问权限比特位.

. shm_lpid,shm_nattch,shm_atime,shm_dtime设置为0.

. shm_ctime设置为当前系统时间.

. shm_segsz设置为0.

返回值:若调用成功则返回一个非0值,称为共享内存标识符,否则返回值为-1.

2.20.shmctl() 共享内存控制操作

功能:共享内存控制操作.

语法:

#include

#include

#include

int shmctl(shmid,cmd,buf)

int shmid,cmd;

struct shmid_ds *buf;

说明:本系统调用提供一系列共享内存控制操作.操作行为由cmd指定. 以下为cmd的有效值:

. IPC_STAT:将shmid相关的数据结构中各个元素的当前值放入由

buf指向的结构中.

. IPC_SET:将shmid相关的数据结构中的下列元素设置为由buf指

向的结构中的对应值.

shm_perm.uid

shm_perm.gid

shm_perm.mode

本命令只能由有效UID等于shm_perm.cuid或shm_perm.uid的进程或有效UID有合适权限的进程操作.

. IPC_RMID:删除由shmid指示的共享内存.将它从系统中删除并破坏相关的数据结构.

本命令只能由有效UID等于shm_perm.cuid或shm_perm.uid的进程或有效UID有合适权限的进程操作.

返回值:若调用成功则返回0,否则返回-1.

例子:本例包括上述所有共享内存操作系统调用:

#include

#include

#include

#define SHMKEY 74

#define K 1024

int shmid;

cleanup()

{

shmctl(shmid,IPC_RMID,0);

exit(0);

}

main()

{

int *pint;

char *addr1,*addr2;

extern char *shmat();

extern cleanup();

for (i=0;i<20;i++) signal(i,cleanup);

shmid=shmget(SHMKEY,128*K,0777|IPC_CREAT);

addr1=shmat(shmid,0,0);

addr2=shmat(shmid,0,0);

printf("addr1 0x%x addr2 0x%x\n",addr1,addr2);

pint=(int*)addr1;

for (i=0;i<256;i++) *pint++=i;

pint=(int*)addr1;

*pint=256;

pint=(int*)addr2;

for (i=0;i<256;i++)

printf("index %d\tvalue%d\n",i,*pint++);

shmdt(addr1);

shmdt(addr2);

pause();

}

2.21.semctl() 信号量控制操作

功能:信号量控制操作.

语法:

#include

#include

#include

int semctl(semid,memnum,cmd,arg)

int semid,semnum,cmd;

union semun {

int val;

struct semid_ds *buf;

ushort *array;

}arg;

说明:本系统调用提供了一个信号量控制操作,操作行为由cmd定义,这些命令是对由semid和semnum指定的信号量做操作的.每个命令都要求有相应的权限级别:

. GETV AL:返回semval的值,要求有读权限.

. SETV AL:设置semval的值到arg.val上.此命令成功执行后,

semadj的值对应的所有进程的信号量全部被清除,要求有修

改权限.

. GETPID:返回sempid的值,要求有读权限.

. GETNCNT:返回semncnt的值,要求有读权限.

. GETZCNT:返回semzcnt的值,要求有读权限.

以下命令在一组信号量中的各个semval上操作:

. GETALL:返回每个semval的值,同时将各个值放入由arg.array

指向的数组中.当此命令成功执行后,semadj的值对应的所有

进程的信号量全部被清除,要求有修改权限.

. SETALL:根据由arg.array指向的数组设置各个semval值.当此

命令成功执行后,semadj的值对应的所有进程的信号量全部

被清除,要求有修改权限.

以下命令在任何情况下都是有效的:

. IPC_STAT:将与semid相关的数据结构的各个成员的值放入由

arg.buf指向的结构中.要求有读权限.

. IPC_SET:设置semid相关数据结构的如下成员,设置数据从

arg.buf指向的结构中读取:

sem_perm.uid

sem_perm.gid

sem_perm.mode

本命令只能由有效UID等于sem_perm.cuid或sem_perm.uid的

进程或有效UID有合适权限的进程操作.

. IPC_RMID:删除由semid指定的信号量标识符和相关的一组信号

量及数据结构.本命令只能由有效UID等于sem_perm.cuid或

sem_perm.uid的进程或有效UID有合适权限的进程操作.

返回值:若调用成功,则根据cmd返回以下值:

GETV AL:semval的值.

GETPID:sempid的值.

GETNCNT:semncnt的值.

GETZCNT:semzcnt的值.

其他:0.

若调用失败则返回-1.

2.22.semget() 取得一组信号量

功能:取得一组信号量.

语法:

#include

#include

#include

int semget(key,nsems,semflg)

key_t key;

int nsems,semflg;

说明:返回和key相关的信号量标识符.

若以下事实成立,则与信号量标识符,与之相关的semid_ds数据结

构及一组nsems信号量将被创建:

. key等于IPC_PRIV ATE.

. 系统内还没有与key相关的信号量,同时(semflg&IPC_CREAT)

为真.

创建时新的信号量相关的semid_ds数据结构被初始化如下:

. 在操作权限结构,sem_perm.cuid和sem_perm.uid设置等于调用

进程的有效UID.

. 在操作权限结构,sem_perm.cgid和sem_perm.gid设置等于调用

进程的有效GID.

. 访问权限比特位sem_perm.mode设置等于semflg的访问权限比

特位.

. sem_otime设置等于0,sem_ctime设置等于当前系统时间.

返回值:若调用成功,则返回一非0值,称为信号量标识符;否则返回-1.

2.2

3.semop() 信号量操作

功能:信号量操作.

语法:

#include

#include

#include

int semop(semid,sops,nsops)

进程与线程的区别 进程的通信方式 线程的通信方式

进程与线程的区别进程的通信方式线 程的通信方式 进程与线程的区别进程的通信方式线程的通信方式2011-03-15 01:04 进程与线程的区别: 通俗的解释 一个系统运行着很多进程,可以比喻为一条马路上有很多马车 不同的进程可以理解为不同的马车 而同一辆马车可以有很多匹马来拉--这些马就是线程 假设道路的宽度恰好可以通过一辆马车 道路可以认为是临界资源 那么马车成为分配资源的最小单位(进程) 而同一个马车被很多匹马驱动(线程)--即最小的运行单位 每辆马车马匹数=1 所以马匹数=1的时候进程和线程没有严格界限,只存在一个概念上的区分度 马匹数1的时候才可以严格区分进程和线程 专业的解释: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执 行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序 的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行 的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在 应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可 以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程 的调度和管理以及资源分配。这就是进程和线程的重要区别。 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的 能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中 必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的 其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以 并发执行 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有 独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响, 而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线 程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程 的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者 《操作系统的设计与实现》。对就个问题说得比较清楚。 +++ 进程概念

进程和线程的选择

鱼还是熊掌:浅谈多进程多线程的选择 关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试基本上够了,但如果在工作中遇到类似的选择问题,那就没有这么简单了,选的不好,会让你深受其害。 经常在网络上看到有的XDJM问“多进程好还是多线程好?”、“Linux下用多进程还是多线程?”等等期望一劳永逸的问题,我只能说:没有最好,只有更好。根据实际情况来判断,哪个更加合适就是哪个好。 我们按照多个不同的维度,来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受) 看起来比较简单,优势对比上是“线程 3.5 v 2.5 进程”,我们只管选线程就是了? 呵呵,有这么简单我就不用在这里浪费口舌了,还是那句话,没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。 1)需要频繁创建销毁的优先用线程 原因请看上面的对比。 这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的

2)需要进行大量计算的优先使用线程 所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。 这种原则最常见的是图像处理、算法处理。 3)强相关的处理用线程,弱相关的处理用进程 什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。 一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。 当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。 4)可能要扩展到多机分布的用进程,多核分布的用线程 原因请看上面对比。 5)都满足需求的情况下,用你最熟悉、最拿手的方式 至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。 需要提醒的是:虽然我给了这么多的选择原则,但实际应用中基本上都是“进程+线程”的结合方式,千万不要真的陷入一种非此即彼的误区。

c语言 多进程和多线程

一.多进程程序的特点 进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。 进程是资源管理的最小单位,线程是程序执行的最小单位。进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文切换开销。 进程的状态系统为了充分的利用资源,对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态. 新建表示进程正在被创建, 运行是进程正在运行, 阻塞是进程正在等待某一个事件发生, 就绪是表示系统正在等待CPU来执行命令, 完成表示进程已经结束了系统正在回收资源. 由于UNIX系统是分时多用户系统, CPU按时间片分配给各个用户使用,而在实质上应该说CPU按时间片分配给各个进程使用, 每个进程都有自己的运行环境以使得在CPU做进程切换时不会"忘记"该进程已计算了一半的"半成品”. 以DOS的概念来说, 进程的切换都是一次"DOS中断"处理过程, 包括三个层次: 1)用户数据的保存: 包括正文段(TEXT), 数据段(DATA,BSS), 栈段(STACK), 共享内 存段(SHARED MEMORY)的保存. 2)寄存器数据的保存: 包括PC(program counter,指向下一条要执行的指令的地址), PSW(processor status word,处理机状态字), SP(stack pointer,栈指针), PCBP(pointer of process control block,进程控制块指针), FP(frame pointer,指向栈中一个函数的local 变量的首地址), AP(augument pointer,指向栈中函数调用的实参位置), ISP(interrupt stack pointer,中断栈指针), 以及其他的通用寄存器等. 3)系统层次的保存: 包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时 间片时能正常运行。既然系统已经处理好所有这些中断处理的过程, 我们做程序 还有什么要担心的呢? 我们尽可以使用系统提供的多进程的特点, 让几个程序精 诚合作, 简单而又高效地把结果给它搞出来。 另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程?将会对UNIX系统机制有一个较深的认识.首先我介绍一下多进程程序的一些突出的特点:

Apache log4cxx在C++多进程多线程下的使用

Apache log4cxx在C++多进程多线程下的使用 1、Apache log4cxx介绍 Apache log4cxx是Apache Logging Services三个日志记录项目之一,完全开源组件。是著名的日志记录组件log4j的c++移植版,用于为C++程序提供日志功能,以便开发者对目标程序进行调试和审计。当前的最新版本为0.10.0。 2、Apache log4cxx 框架组成 Apache Log4cxx有三个关键组件,它们是loggers, appenders和layouts。执行日志操作Logger是log4cxx的核心类。looger有层次结构,最顶层为RootLogger;logger是分七个级别,分别是debug、info、warn、error、fatal、all、off,最常用的应该是debug()和info();而warn()、error()、fatal()仅在相应事件发生后才使用。每个logger可以附加多个Appender。Appender 代表了日志输出的目标,如输出到文件、控制台,数据库等等。对于每一种appender,都可以通过layout进行格式设置,根据自己需求定制不同日志内容。 使用中用到的类有BasicConfigurator、PropertyConfigurator、DOMConfigurator等,用于对log4cxx进行配置。其中BasicConfigurator提供了一种简单配置,包括使用ConsoleAppder作为root appender和PatternLayout 作为缺省布局, PropertyConfigurator使用properties文件作为配置方式,DOMConfigurator则使用properties文件作为配置方式,具体配置文档信息请查阅相关资料。 3、多进程多线程使用设计 Apache Log4cxx 提供的常用供日志调用方法,logger->info(),logger->debug(),logger->warn(),logger->error(),与上述方法类似的还有相应的宏调用LOG4CXX_DEBUG()、LOG4CXX_INFO()、LOG4CXX_WARN()、LOG4CXX_ERROR()。每个方法与宏的参数要求是全字符串类型,对于不同的日志信息相应调用不同的日志记录方法,即可得到不同级别、不同类型的日志信息。 配置文件的设置Apache Log4cxx提供 static void configure(helpers::Properties& properties)函数,参数中传入配置文件的绝对路径或是相对路径、文件名,日志文件名称 Apache Log4cxx 提供 static LoggerPtr getLogger(const std::wstring& name); 参数中传入日志文件名,用日志文件名称实例化LoggerPtr对象 LoggerPtr logger(Logger::getLogger(trace)),即可用通过logger对象调用相应类型日志方法,宏调用也在实例化日志对象后才能对设置信息有作用大家看到了,方法参数要求是一个字符串类型,不便于C++记录日志,也不习惯于C++程序员使用。配置文件与日志文件名设备,都用到了静态方法,多线程共用一个日志对象,每个线程一个日志文件,上述静态方法是不能满足这个需求的,多个进程里面不用的线程调用此方法,每个线程一个日志文件,就更不能满足需求,所以我们必须自己设计一种结构来封装Apache Log4cxx提供的方法,满足多进程多线程下,一个线程一个日志文件的需求。设计一种方便的参数传入模式,使C++程序员能方便使用日志组件提供方法。 我的设计模型如下,用动态库封装日志方法,不同进程、不同线程实例化一

实验二 编程实现进程(线程)同步和互斥

《操作系统》实验内容 实验二编程实现进程(线程)同步和互斥 1.实验的目的 (1)通过编写程序实现进程同步和互斥,使学生掌握有关进程(线程)同步与互斥的原理,以及解决进程(线程)同步和互斥的算法,从而进一步巩固进程(线程)同步和互斥等有关的内容。 (2)了解Windows2000/XP中多线程的并发执行机制,线程间的同步和互斥。 (3)学习使用Windows2000/XP中基本的同步对象,掌握相应的API函数。 (4)掌握进程和线程的概念,进程(线程)的控制原语或系统调用的使用。 (5)掌握多道程序设计的基本理论、方法和技术,培养学生多道程序设计的能力。2.实验内容 在Windows XP、Windows 2000等操作系统下,使用的VC、VB、java或C等编程语言,采用进程(线程)同步和互斥的技术编写程序实现生产者-消费者问题或哲学家进餐问题或读者-写者问题或自己设计一个简单进程(线程)同步和互斥的实际问题。 3.实验要求 (1)经调试后程序能够正常运行。 (2)采用多进程或多线程方式运行,体现了进程(线程)同步和互斥的关系。 (3)程序界面美观。 4.实验步骤 (1)需求分析:了解基本原理,确定程序的基本功能,查找相关资料,画出基本的数据流图; (2)概要设计:确定程序的总体结构、模块关系和总体流程; (3)详细设计:确定模块内部的流程和实现算法; (4)上机编码和调试; (5)运行测试; (6)编写实验报告。 5.实验报告要求 格式符合《实验报告格式》书;书写规范,排版美观,有较强的文字表达能力,能够正确地表达自己的思想,图表符合规范。 6.实验说明 本实验分两次进行,每次要求填写一份实验报告,报告中的实验名分别为:编程实现进程同步和互斥1和编程实现进程同步和互斥2,其他内容依据实验进度具体填写。

多进程与多线程性能比较

1.多进程与多线程并发速度的实验对比 在多进程与多线程并发运行速度的问题上,一般认为线程的创建销毁速度快,进程的创建销毁速度慢,多线程的速度要优于多进程。为了得到明确结果,在192.168.1.141的主机上使用了thread.c和fork.c两个程序进行测试。(测试程序基于论文《Linux系统下多线程与多进程性能分析》作者“周丽焦程波兰巨龙”) fork.c #include #include #include #define P_NUMBER 127 #define COUNT 50 #define P_NUMBER 127 /* 并发进程数量*/ #define COUNT 50 /* 每进程打印字符串次数*/ char *s = "hello linux\0"; int main(void) { int i = 0, j = 0; logFile = fopen(TEST_LOGFILE, "a+"); for (i = 0;i < P_NUMBER; i ++){ if (fork() == 0){ for (j = 0;j < COUNT; j ++){ printf("[%d]%s\n", j, s); fprintf(logFile, "[%d]%s\n", j, s); } exit(0); } } for (i = 0;i < P_NUMBER; i ++){ wait(0); } } thread.c #include #include #include #include #define P_NUMBER 127 /* 并发线程数量*/ #define COUNT 50 /* 每线程打印字符串次数*/ #define Test_Log "logFile.log"

JAVA多线程(一)基本概念和上下文切换性能损耗

JAVA多线程(一)基本概念和上下文切换性能损耗 1 多线程概念 在理解多线程之前,我们先搞清楚什么是线程。根据维基百科的描述,线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是行程中的实际运行单位。一条线程指的是进程中一个单一顺序的控制流,一個进程中可以并行多个线程,每条线程并行执行不同的任务。每个线程共享堆空间,拥有自己独立的栈空间。 这里反复出现的概念是线程和进程,我们在这里列出它们的区别: 线程划分尺度小于进程,线程隶属于某个进程; 进程是CPU、内存等资源占用的基本单位,线程是不能独立占有这些资源的; 进程之间相互独立,通信比较困难,而线程之间共享一块内存区域,通信方便; 进程在执行过程中,包含比较固定的入口、执行顺序和出口,而进程的这些过程会被应用程序控制。 多线程是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时

间执行多个线程,进而提升整体处理效能。 2 为什么要使用多线程 随着计算机硬件的发展,多核CPU已经屡见不鲜了,甚至手机处理器都早已是多核的天下。这就给我们使用多线程提供了硬件基础,但是,只是因为硬件让我们可以实现多线程,就要这样做吗?一起来看看多线程的优点: 更高的运行效率。在多核CPU上,线程之间是互相独立的,不用互相等待,也就是所谓的“并行“。举个例子,一个使用多线程的文件系统可以实现高吞吐量和低延迟。这是因为我们可以用一个线程来检索存储在高速介质(例如高速缓冲存储器)中的数据,另一个线程检索低速介质(例如外部存储)中的数据,二者互不干扰,也不用等到另一个线程结束才执行; 多线程是模块化的编程模型。在单线程中,如果主执行线程在一个耗时较长的任务上卡住,或者因为网络响应问题陷入长时间等待,此时程序不会响应鼠标和键盘等操作。多线程通过将程序分成几个功能相对独立的模块,单独分配一个线程去执行这个长耗时任务,不影响其它线程的执行,就可以避免这个问题; 与进程相比,线程的创建和切换开销更小。使用多线程为多个客户端服务,比使用多进程消耗的资源少得多。由于启动

基于Linux的多进程网络通信

信息工程学院 嵌入式系统设计课程设计报告 题目:基于linux系统移植的多进程通信学号:------------ 学生姓名:-------- 专业名称:计算机科学与技术 班级:-------

目录 一、课程研究意义及现状 (3) 1.1课题研究意义 (3) 1.2课题研究现状 (3) 二、系统总体方案设计及功能模块介绍 (4) 2.1 系统概述及总体方案设计 (4) 2.2功能模块介绍 (4) 三、系统软件设计与实现 (5) 3.1主程序设计与实现 (5) 3.3文件上传程序设计与实现 (8) 3.4文件下载程序设计与实现 (9) 3.5多进程简易聊天设计与实现 (10) 四、系统测试 (12) 4.1系统软件测试 (12) 4.1.1文件上传测试 (12) 4.1.2文件下载测试 (13) 4.1.3多进程简易聊天 (14) 4.2系统硬件测试 (15) 五、总结和展望 (16) 六、参考文献 (17) 七、源代码 (18)

一、课程研究意义及现状 1.1课题研究意义 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX 和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统 1.2课题研究现状 在网络无所不在的今天,在Internet上,有Faceboo k、微信、Twitter、QQ等网络聊天软件,极大程度上方便了处于在世界各地的友人之间的相互联系,也使世界好像一下子缩小了,不管你在哪里,只要你上了网,打开这些软件,就可以给你的朋友发送信息,不管对方是否也同时在线,只要知道他有号码。 Linux操作系统作为一个开源的操作系统被越来越多的人所应用,它的好处在于操作系统源代码的公开化!只要是基于GNU公约的软件你都可以任意使用并修改它的源代码。但对很多习惯于Windows操作系统的人来说,Linux的操作不够人性化、交互界面不够美观,这给Linux操作系统的普及带来了很大的阻碍。因此制作一个Linux操作系统下的简易聊天程序,通过设计这样的一个应用程序还能更好的学习网络编程知识和掌握Linux平台上应用程序设计开发的过程,将大学三年所学知识综合运用,以达到检验学习成果的目的。

线程、进程、多线程、多进程和多任务之间的区别与联系

线程、进程、多线程、多进程和多任务之间的区别与联系

可能学习操作系统开发的读者都听说过这些专业名词,但又多少人理解了? 首先,从定义开始,先看一下教科书上进程和线程定义:进程:资源分配的最小单位。线程:程序执行的最小单位。 1 进程进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。 举例说明进程:想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕,他有做生日蛋糕的食谱,厨房里有所需的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序(即用适当形式描述的算法)计算机科学家就是处理器(CPU),而做蛋糕的各种原料就是输入数据。 进程就是厨师阅读食谱、取来各种原料以及烘制蛋糕等一系列动作的总和。现在假设计算机科学家的儿子哭着跑了进来,说他的头被一只蜜蜂蛰了。计算机科学家就记录下他照着食谱做到哪儿了(保存进程的当前状态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这里,我们看到处理机制是从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程拥有各自的程序(食谱和急救手册)。当蜜蜂蛰伤处理完之后,这位计算机科学

家又回来做蛋糕,从他离开时的那一步继续做下去。 2 线程线程是CPU调度的最小单位(程序执行流的最小单元),它被包含在进程之中,是进程中的实际运作单元。一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 一个标准的线程有线程ID、当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单元,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现处间断性。 线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。举例说明线程:假设,一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。若只有一个进程,势必造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。若有多个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协作涉及到了进程通信问题,而且有共同都需要拥有的东西——-文本内容,不停的切换造成性能上的损失。若有一种机制,可以使任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带来的性能损耗,那就好了。这种机制就是线程。 总的来说:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

JAVA的多进程运行模式分析与编程

一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。 在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname。如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。 //Test1.java文件 import java.io.*; public class Test { public static void main(String[]args) { FileOutputStream fOut=new FileOutputStream("c:\\Test1.txt"); fOut.close(); System.out.println("被调用成功!"); } } //Test_Exec.java public class Test_Exec { public static void main(String[]args) { Runtime run=Runtime.getRuntime(); Process p=run.exec("java test1"); } } 通过java Test_Exec运行程序后,发现在C盘多了个Test1.txt文件,但在控制台中并未出现"被调用成功!"的输出信息。因此可以断定,Test已经被执行成功,但因为某种原因,Test的输出信息未在Test_Exec的控制台中输出。这个原因也很简单,因为使用exec建立的是Test_Exec的子进程,这个子进程并没有自己的控制台,因此,它并不会输出任何信息。 如果要输出子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如下如示: //Test_Exec_Out.java import java.io.*; public class Test_Exec_Out { public static void main(String[]args) { Runtime run=Runtime.getRuntime(); Process p=run.exec("java test1");

进程与线程的区别[试题]

进程与线程的区别[试题] 进程与线程的区别: 通俗的解释 一个系统运行着很多进程,可以比喻为一条马路上有很多马车 不同的进程可以理解为不同的马车而同一辆马车可以有很多匹马来拉----这些马就是线程 假设道路的宽度恰好可以通过一辆马车道路可以认为是临界资源 那么马车成为分配资源的最小单位(进程) 而同一个马车被很多匹马驱动(线程)----即最小的运行单位 每辆马车马匹数>=1 所以马匹数=1的时候进程和线程没有严格界限,只存在一个概念上的区分度马匹数>1的时候才可以严格区分进程和线程 专业的解释: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者《操作系统的设计与实现》。对就个问题说得比较清楚。 +++++++++++++++++++++++++++++++++++++++++++++++ 进程概念 进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I,O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。所以,进程是系统中的并发执行的单位。

实验五、多线程并发服务器编程

实验五、多线程并发服务器编程 一、实验目的 1、学习Linux操作系统的多线程的基本概念以及进程与线程的区别; 2、掌握编写多线程程序的一般方法; 3、熟悉多线程并发服务器的设计思路,以及多线程程序的编译方法。 二、实验容 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。现在多线程技术已经被许多操作系统所支持,包括Windows/NT以及Unix/Linux。 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计一个进程的开销大约是一个线程开销的30倍左右。 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。 1、编写一个最简单的多线程程序 请仔细运行、分析下列程序,指出多进程和多线程如何区分?何谓父线程和子线程?当父线程终止会导致子线程发生何种情况? /***************************************************************** 文件名:pthread_example.c 演示了pthread_create函数创建子线程的使用 *******************************************************************/ #include #include //创建多线程

linux多进程和进程间通信

物联网学院 20 15 / 2016 学年第 1 学期实验报告 课程名称:linux编程基础 实验名称:linux多进程和进程间通信 班级学号: 学生姓名: 指导教师 日期:20 15年12 月

实验六linux多进程与进程间通信 一、实验目的 熟悉linux系统编程方法 熟悉linux常用的系统调用 二、实验环境 PC机一台 安装虚拟机版的linux操作系统一台 三、实验预习 熟悉进程创建/线程创建系统调用 熟悉进程间通信系统的调用 四、实验内容 Linux fork调用 进程间通信系统调用:管道通信 五、实验步骤 编写一个程序,实现如下功能: 创建两个子进程,父进程向管道中按照顺序输入数字1 2 3 4 5 6……,另外两个子进程分别从管道中按照顺序读出奇数和偶数,及子进程1读出的数据应该是1 3 5 7 9……,而子进程读出的数据应该是2 4 6 8 10 ……数据,要求按先读奇数,再读偶数的顺序进行 程序代码如下: #include #include

#include #include int pid1,pid2; main( ) { int fd[2],i=0,j=0; char outpipe[100],inpipe1[100],inpipe2[100],outpipe1[100]; pipe(fd); /*创建一个管道*/ while ((pid1=fork( ))==-1); /*向管道中写入字符123456789*/ lockf(fd[1],1,0); sprintf(outpipe,"123456789"); write(fd[1],outpipe,50); lockf(fd[1],0,0); /*----------------------------------*/ /*子进程1先读取管道中所有的字符,再重新把不需要的字符写入管道*/ if(pid1==0) { lockf(fd[0],1,0); read(fd[0],inpipe1,10); for(i=0;i<10;i++)//把不需要的字符写入管道

曾帆_1006100012_多进程与多线程

广州大学学生实验报告 开课学院及实验室:计算机科学与工程实验室2013 年5月5日 学院计算机科学与 教育软件学院 年级/专 业/班 软工 104 姓名曾帆学号1006100012 实验课 程名称 Linux操作系统分析实验成绩 实验项 目名称 Linux系统下多进程与多线程编程指导老师陶文正 一、实验目的 1、理解Linux下进程的结构; 2、理解Linux下产生新进程的方法(系统调用—fork函数); 3、掌握如何启动另一程序的执行; 4、理解Linux下线程的结构; 5、理解Linux下产生新线程的方法; 6、理解Linux系统下多进程与多线程的区别 二、使用仪器、器材 1.设备:带网卡的PC若干、交换机一台。 2.工具:网线若干,已经安装好Red Hat Linux 9.0系统的PC一台。 三、实验内容及原理 1、利用fork函数编写一个简单的多进程程序,用ps命令查看系统中进程的运行 状况,并分析输出结果。 2、在上面的多进程程序中利用exec函数,启动另一个程序的执行。用ps命令 显示本机运行的所有进程的详细列表,并分析列表中不同进程的内存占用情况。 3、编写一个最简单的多线程程序。理解多线程的运行和输出情况; 4、利用信号量机制控制多线程的运行顺序,分析多线程中数据的共享情况; 5、分析Linux系统下多进程与多线程中的区别。 四、实验过程 (一)Linux系统下多进程编程: 在Linux中要使用exec类的函数来启动另一程序的执行,exec类的函数不止一个,但大致相同,在Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp。一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段

windows多进程编程

一、进程的概念 进程是是一个正在运行的程序的实例(飘~~~),是系统分配资源的单位(线程是执行的单位),包括内存,打开的文件、处理机、外设等,进程由两部分组成: 1、进程的内核对象:即我们通常所讲的PCB(进程控制块),该结构只能由该内核访问,他是操作系统用来管理进程的一个数据结构,操作系统通过该数据结构来感知和管理进程;它的成员负责维护进程的各种信息,包括进程的状态(创建、就绪、运行、睡眠、挂起、僵死等)、消息队列等;同时也是系统用来存放关于进程的统计信息的地方。 注:具体结构参照Linux的task_struct数据结构。 2、进程的地址空间:包含所有可执行模块或DLL模块的代码和数据,以及动态内存分配的空间,如线程堆栈和堆分配的空间。共有4G,0-2G为用户区,2-4G 为系统区。 二、进程的创建过程 1、系统创建进程内核对象(PCB进程控制块)。 2、系统为新进程创建虚拟地址空间,帮将可执行文件或任何必要的DLL文件的代码和数据加载到该进程的地址空间。 3、系统为新进程的主线程创建一个线程内核对象(TCB线程控制块)。 4、通过执行C/C++运行期启动代码,该主线程开始运行。注:在Windows环境下,尽量用多线程而不是多进程。 三、与进程相关的API 1、创建进程 BOOL CreateProcess( PCTSTR psApplicationName, //可执行文件的名字 PTSTR pszCommandLine, //命令行字符串 PSECURITY_ATTRIBUTES psaProcess, //进程对象的安全性 PSECURITY_ATTRIBUTES psaThread, //线程对象的安全性 BOOL bInheritHandles, //句柄可继承性 DWORD fdwCreate, //标识符(优先级) PVOID pvEnvironment, //指向环境字符串 PCTSTR pszCurDir, //子进程当前目录 PSTARTUPINFO psiStartInfo, PPROCESS_INFORMATION ppiProcInfo); //进程线程句柄及ID 2、打开进程 HANDLE OpenProcess( DWORD dwDesiredAccess, //访问安全属性 BOOL bInheritHandle, //继承属性 DWORD hProcessId); //进程ID 注:获取hPronessId指定的进程的内核对象的句柄 3、终止进程 (1)、主线程的进入点函数返回 (2)、进程自己终止自己

关于多进程和多线程

关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试基本上够了,但如果在工作中遇到类似的选择问题,那就没有这么简单了,选的不好,会让你深受其害。 经常在网络上看到有的XDJM问“多进程好还是多线程好?”、“Linux下用多进程还是多线程?”等等期望一劳永逸的问题,我只能说:没有最好,只有更好。根据实际情况来判断,哪个更加合适就是哪个好。 我们按照多个不同的维度,来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受)。 看起来比较简单,优势对比上是“线程 3.5 v 2.5 进程”,我们只管选线程就是了? 呵呵,有这么简单我就不用在这里浪费口舌了,还是那句话,没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。 1)需要频繁创建销毁的优先用线程 原因请看上面的对比。 这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的 2)需要进行大量计算的优先使用线程 所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。 这种原则最常见的是图像处理、算法处理。 3)强相关的处理用线程,弱相关的处理用进程 什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。 一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。 当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。 4)可能要扩展到多机分布的用进程,多核分布的用线程

c语言多进程多线程编程

C语言多进程编程 一.多进程程序的特点 进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。 进程是资源管理的最小单位,线程是程序执行的最小单位。进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器系统和减小上下文切换开销。 进程的状态系统为了充分的利用资源,对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态. 新建表示进程正在被创建, 运行是进程正在运行, 阻塞是进程正在等待某一个事件发生, 就绪是表示系统正在等待CPU来执行命令, 完成表示进程已经结束了系统正在回收资源. 由于UNIX系统是分时多用户系统, CPU按时间片分配给各个用户使用,而在实质上应该说CPU按时间片分配给各个进程使用, 每个进程都有自己的运行环境以使得在CPU做进程切换时不会"忘记"该进程已计算了一半的"半成品”. 以DOS的概念来说, 进程的切换都是一次"DOS中断"处理过程, 包括三个层次: 1)用户数据的保存: 包括正文段(TEXT), 数据段(DATA,BSS), 栈段(STACK), 共享内 存段(SHARED MEMORY)的保存. 2)寄存器数据的保存: 包括PC(program counter,指向下一条要执行的指令的地址), PSW(processor status word,处理机状态字), SP(stack pointer,栈指针), PCBP(pointer of process control block,进程控制块指针), FP(frame pointer,指向栈中一个函数的local 变量的首地址), AP(augument pointer,指向栈中函数调用的实参位置), ISP(interrupt stack pointer,中断栈指针), 以及其他的通用寄存器等. 3)系统层次的保存: 包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时 间片时能正常运行。既然系统已经处理好所有这些中断处理的过程, 我们做程序 还有什么要担心的呢? 我们尽可以使用系统提供的多进程的特点, 让几个程序精 诚合作, 简单而又高效地把结果给它搞出来。 另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程?将会对UNIX系统机制有一个较深的认识.首先我介绍一下多进程程序的一些突出的特点:

linux多线程和多进程

linux下多进程、多线程编程 linux下进程 (一)理解Linux下进程的结构 Linux下一个进程在内存里有三部份的数据,就是“数据段”,“堆栈段”和“代码段”,其实学过汇编语言的人一定知道,一般的CPU像I386,都有上述三种段寄存器,以方便操作系统的运行。“代码段”,顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。 堆栈段存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。这其中有许多细节问题,这里限于篇幅就不多介绍了。系统如果同时运行数个相同的程序,它们之间就不能使用同一个堆栈段和数据段。 (二)如何使用fork 在Linux下产生新的进程的系统调用就是fork函数,这个函数名是英文中“分叉”的意思。为什么取这个名字呢?因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就“分叉”了,所以这个名字取得很形象。下面就看看如何具体使用fork,这段程序演示了使用fork的基本框架: void main() { int i; if ( fork() == 0 ) {/* 子进程程序*/ for ( i = 1; i " ); fgets( command, 256, stdin ); command[strlen(command)-1] = 0; if ( fork() == 0 ) {/* 子进程执行此命令*/ execlp( command, command ); /* 如果exec函数返回,表明没有正常执行命令,打印错误信息*/ perror( command ); exit( errorno ); } else {/* 父进程,等待子进程结束,并打印子进程的返回值*/ wait ( &rtn ); printf( " child process return %d\n",. rtn ); } } } 此程序从终端读入命令并执行之,执行完成后,父进程继续等待从终端读入命令。熟悉DOS和WINDOWS系统调用的朋友一定知道DOS/WINDOWS也有exec类函数,其使用方法是类似的,但DOS/WINDOWS还有spawn类函数,因为DOS是单任务的系统,它只能将“父进程”驻留在机器内再执行“子进程”,这就是spawn类的函数。WIN32已经是多任务的系统了,但还保留了spawn类函数,WIN32中实现spawn函数的方法同前述UNIX中的方法差不多,开设子

相关文档
最新文档