【IT专家】Linux网络编程 fork() 和 exec() 函数实例分析
- 格式:pdf
- 大小:215.29 KB
- 文档页数:2
linux源代码-fork.c源代码/** linux/kernel/fork.c*//--fork()用于创建子进程* (C) 1991 Linus Torvalds*//** 'fork.c' contains the help-routines for the 'fork' system call* (see also system_call.s), and some misc functions('verify_area').* Fork is rather simple, once you get the hang of it, but the memory * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */#include <errno.h>#include <linux/sched.h>#include <linux/kernel.h>#include <asm/segment.h>#include <asm/system.h>//--写页面验证,若页面不可写,则复制页面extern void write_verify(unsigned long address);long last_pid=0;//--进程空间区域写前验证函数void verify_area(void * addr,int size){unsigned long start;start = (unsigned long) addr;size += start & 0xfff;start &= 0xfffff000;start += get_base(current->ldt[2]); //--逻辑地址到线性地址的转换while (size>0) {size -= 4096;write_verify(start);start += 4096;}}int copy_mem(int nr,struct task_struct * p) //--复制内存页表{//--由于采用写时复制技术,这里只复制目录和页表项,不分配内存unsigned long old_data_base,new_data_base,data_limit;unsigned long old_code_base,new_code_base,code_limit;code_limit=get_limit(0x0f);//--取段限长data_limit=get_limit(0x17);old_code_base = get_base(current->ldt[1]);old_data_base = get_base(current->ldt[2]);if (old_data_base != old_code_base)panic("We don't support separate I&D");if (data_limit < code_limit)panic("Bad data_limit");new_data_base = new_code_base = nr * TASK_SIZE;p->start_code = new_code_base;set_base(p->ldt[1],new_code_base);set_base(p->ldt[2],new_data_base);if(copy_page_tables(old_data_base,new_data_base,data_limit)) { //--复制页表free_page_tables(new_data_base,data_limit);return -ENOMEM;}return 0;}/** Ok, this is the main fork-routine. It copies the system process* information (task[nr]) and sets up the necessary registers. It * also copies the data segment in it's entirety.*///--fork()子程序,它复制系统进程信息,设置寄存器,复制数据段(代码段)int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,long ebx,long ecx,long edx, long orig_eax,long fs,long es,long ds,long eip,long cs,long eflags,long esp,longss) //--复制进程{struct task_struct *p;int i;struct file *f;p = (struct task_struct *)get_free_page(); //--为新任务数据结构分配内存if (!p)return -EAGAIN;task[nr] = p;*p = *current; /* NOTE! this doesn't copy the supervisor stack */p->state = TASK_UNINTERRUPTIBLE;p->pid = last_pid;p->counter = p->priority;p->signal = 0;p->alarm = 0;p->leader = 0; /* process leadership doesn't inherit */p->utime = p->stime = 0;p->cutime = p->cstime = 0;p->start_time = jiffies;p->tss.back_link = 0;p->tss.esp0 = PAGE_SIZE + (long) p;p->tss.ss0 = 0x10;p->tss.eip = eip;p->tss.eflags = eflags;p->tss.eax = 0;p->tss.ecx = ecx;p->tss.edx = edx;p->tss.ebx = ebx;p->tss.esp = esp;p->tss.ebp = ebp;p->tss.esi = esi;p->tss.edi = edi;p->tss.es = es & 0xffff;p->tss.cs = cs & 0xffff;p->tss.ss = ss & 0xffff;p->tss.ds = ds & 0xffff;p->tss.fs = fs & 0xffff;p->tss.gs = gs & 0xffff;p->tss.ldt = _LDT(nr);p->tss.trace_bitmap = 0x80000000;if (last_task_used_math == current)__asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));if (copy_mem(nr,p)) {task[nr] = NULL;free_page((long) p);return -EAGAIN;}for (i=0;i<NR_OPEN;i++) //--如果父进程中有文件是打开的,则将对应文件的打开次数增1if (f=p->filp[i])f->f_count++;if (current->pwd)current->pwd->i_count++;if (current->root)current->root->i_count++;if (current->executable)current->executable->i_count++;if (current->library)current->library->i_count++;set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));//--在GDT表中设置新任务的TSS和LDTset_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));p->p_pptr = current;p->p_cptr = 0;p->p_ysptr = 0;p->p_osptr = current->p_cptr;if (p->p_osptr)p->p_osptr->p_ysptr = p;current->p_cptr = p;p->state = TASK_RUNNING; /* do this last, just in case */return last_pid;}intfind_empty_process(void)//--为新进程取得不重复的进程号last_pid{int i;repeat:if ((++last_pid)<0) last_pid=1;for(i=0 ; i<NR_TASKS ; i++)if (task[i] && ((task[i]->pid == last_pid) ||(task[i]->pgrp == last_pid)))goto repeat;for(i=1 ; i<NR_TASKS ; i++)if (!task[i])return i;return -EAGAIN; }。
fork函数实例fork函数是操作系统中一个非常重要的函数,它的作用是创建一个新的进程。
在本文中,我们将详细介绍fork函数的使用方法和原理。
一、fork函数的使用方法在C语言中,fork函数的原型如下:```c#include <sys/types.h>#include <unistd.h>pid_t fork(void);```调用fork函数会创建一个新的进程,这个新进程是调用进程的一个副本。
新进程与调用进程几乎完全相同,包括代码、数据和打开的文件等。
fork函数的返回值有以下三种情况:- 若返回值为-1,表示fork函数调用失败,新进程创建失败;- 若返回值为0,表示当前进程为新创建的子进程;- 若返回值大于0,表示当前进程为父进程,返回值为子进程的进程ID。
我们可以根据fork函数的返回值来判断当前进程是父进程还是子进程,并相应地对它们进行不同的处理。
下面是一个简单的示例代码:```c#include <stdio.h>#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {printf("fork error");} else if (pid == 0) {printf("This is child process. PID: %d\n", getpid());} else {printf("This is parent process. PID: %d, Child PID: %d\n", getpid(), pid);}return 0;}```运行上述代码,可以看到输出结果中会有两行信息,分别表示父进程和子进程的相关信息。
二、fork函数的原理fork函数的原理是通过创建一个与当前进程几乎完全相同的新进程来实现的。
exec命令详解原⽂链接:exec:在bash下输⼊man exec,找到exec命令解释处,可以看到有”No new process is created.”这样的解释,这就是说exec命令不产⽣新的⼦进程。
那么exec与source的区别是什么呢?exec命令在执⾏时会把当前的shell process关闭,然后换到后⾯的命令继续执⾏。
1. 系统调⽤exec是以新的进程去代替原来的进程,但进程的PID保持不变。
因此,可以这样认为,exec系统调⽤并没有创建新的进程,只是替换了原来进程上下⽂的内容。
原进程的代码段,数据段,堆栈段被新的进程所代替。
⼀个进程主要包括以下⼏个⽅⾯的内容:(1)⼀个可以执⾏的程序(2) 与进程相关联的全部数据(包括变量,内存,缓冲区)(3)程序上下⽂(程序计数器PC,保存程序执⾏的位置)2. exec是⼀个函数簇,由6个函数组成,分别是以excl和execv打头的。
执⾏exec系统调⽤,⼀般都是这样,⽤fork()函数新建⽴⼀个进程,然后让进程去执⾏exec调⽤。
我们知道,在fork()建⽴新进程之后,⽗进各与⼦进程共享代码段,但数据空间是分开的,但⽗进程会把⾃⼰数据空间的内容copy到⼦进程中去,还有上下⽂也会copy到⼦进程中去。
⽽为了提⾼效率,采⽤⼀种写时copy的策略,即创建⼦进程的时候,并不copy⽗进程的地址空间,⽗⼦进程拥有共同的地址空间,只有当⼦进程需要写⼊数据时(如向缓冲区写⼊数据),这时候会复制地址空间,复制缓冲区到⼦进程中去。
从⽽⽗⼦进程拥有独⽴的地址空间。
⽽对于fork()之后执⾏exec后,这种策略能够很好的提⾼效率,如果⼀开始就copy,那么exec之后,⼦进程的数据会被放弃,被新的进程所代替。
3. exec与system的区别(1) exec是直接⽤新的进程去代替原来的程序运⾏,运⾏完毕之后不回到原先的程序中去。
(2) system是调⽤shell执⾏你的命令,system=fork+exec+waitpid,执⾏完毕之后,回到原先的程序中去。
3. 进程控制上一页第30 章进程下一页3. 进程控制3.1. fork函数#include <sys/types.h>#include <unistd.h>pid_t fork(void);fork调用失败则返回-1,调用成功的返回值见下面的解释。
我们通过一个例子来理解fork是怎样创建新进程的。
例30.3. fork#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main(void){pid_t pid;char *message;int n;pid = fork();if (pid < 0) {perror("fork failed");exit(1);}if (pid == 0) {message = "This is the child\n"; n = 6;} else {message = "This is the parent\n"; n = 3;}for(; n > 0; n--) {printf(message);sleep(1);}return 0;}$ ./a.outThis is the childThis is the parentThis is the childThis is the parentThis is the childThis is the parentThis is the child$ This is the childThis is the child这个程序的运行过程如下图所示。
图30.4. fork父进程初始化。
父进程调用fork,这是一个系统调用,因此进入内核。
内核根据父进程复制出一个子进程,父进程和子进程的PCB信息相同,用户态代码和数据也相同。
linux中exec命令的详细解释
linux的exec命令其实是bash的一个内建命令。
下面由店铺整理了linux中exec命令的详细解释,希望对你有帮助。
linux中exec命令的详细解释
exec:是bash的内建命令,可以通过man builtin页面来查看所有bash内建命令的帮助文档。
exec还可以用在文件描述符当中。
常用格式:exec [-cl] [-a name] [command [arguments]]
如果指定了command,它将用当前的command替换当前的shell,但是不会产生新的进程,如果有arguments参数,将会作为command的参数。
选项:
-l:将会在传递给command命令的第0个参数前面加上一个dash('-'),有点像在用su的时候(su - username)
-c:将会使command命令在一个空环境中执行
-a:shell会将name作为第0个参数传递给要执行的command 命令
linux中exec命令的详解实例
首先使用echo命令将文本“Linux C++”进行输出,输入如下命令:
echo Linux C++ #输出指定信息
执行上面的指令后,输出如下信息:
Linux C++ #输出信息
然后再使用exec命令调用echo命令输出同样的信息,并且对输出的信息进行对比,输入指令如下所示:
exec -c echo Linux C++ #调用命令
执行以上命令后,其输出信息如下:
Linux C++ #使用指定指令输出信息
通过比较两者执行后的结果来看,所实现的功能是相同的,即使用exec命令调用echo命令成功。
linux中fork的作用在Linux中,fork(是一个非常重要的系统调用。
它的作用是创建一个新的进程,这个新的进程被称为子进程,而原始进程被称为父进程。
fork(系统调用会在父进程和子进程之间复制一份相同的当前执行状态,包括程序的代码、数据、堆栈以及其他相关资源。
当一个进程调用fork(时,操作系统会将当前的进程映像复制一份,包括进程的地址空间、文件描述符、信号处理器等。
然后操作系统会分配一个唯一的进程ID(PID)给子进程,父进程和子进程会分别返回子进程的PID和0。
子进程会从fork(调用的位置开始执行,而父进程则继续执行接下来的指令。
fork(的作用有以下几个方面:1. 多任务处理:通过fork(,一个进程可以生成多个子进程,每个子进程可以执行不同的任务。
这种多任务处理的能力是Linux操作系统的基石之一,它允许同时运行多个进程,从而提高系统的并发性和响应性能。
2. 进程间通信:fork(可以为不同的进程提供通信机制。
子进程可以通过进程间通信(IPC)机制与父进程进行数据交换,包括管道、消息队列、共享内存等。
这样实现了进程间的数据共享和协同工作。
3. 服务器模型:fork(在服务器模型中起到关键作用。
通过fork(,一个服务器进程可以创建多个子进程来处理客户端请求。
子进程在接收到请求后,可以独立地为客户端提供服务,这样能够极大地提高服务器的吞吐量和并发处理能力。
4. 资源管理:通过fork(,Linux可以对资源进行有效的管理。
当一个进程需要一个完全相同的副本来执行其他任务时,可以使用fork(来复制当前进程的状态。
这种状态的复制可以节省时间和资源,避免了重新加载和初始化的开销。
5. 守护进程创建:守护进程是在后台执行的长时间运行的进程,不依赖于任何终端。
通过调用fork(,父进程可以使自己成为一个后台进程,并终止自己,而子进程则变为一个孤儿进程并被init进程接管。
这样,守护进程就能够在系统启动后一直运行,提供服务。
linux操作系统下fork函数理解在Linux操作系统中,fork函数是一个非常重要的系统调用,它用于创建一个新的进程。
本文将详细解释fork函数的作用、用法和实现原理,并介绍如何利用fork函数实现进程间通信以及避免一些常见的问题。
一、fork函数的作用和用法在Linux系统中,fork函数用于创建一个新的进程,该进程是调用fork函数的进程的一个副本。
具体而言,fork函数会创建一个新的进程,称为子进程,而调用fork函数的进程被称为父进程。
子进程从fork函数返回的地方开始执行,而父进程则继续执行fork函数之后的代码。
简单来说,fork函数的作用就是将一个进程复制成两个几乎完全相同的进程,但它们具有不同的进程ID(PID)。
fork函数的用法非常简单,只需要在程序中调用fork()即可。
具体代码如下所示:```c#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程代码} else if (pid > 0) {// 父进程代码} else {// fork失败的处理代码}return 0;}```在上述代码中,首先使用pid_t类型的变量pid存储fork函数的返回值。
如果pid等于0,则表示当前执行的是子进程的代码;如果pid大于0,则表示当前执行的是父进程的代码;如果pid小于0,则表示fork函数调用失败。
二、fork函数的实现原理在Linux系统中,fork函数的实现是通过复制父进程的内存空间来创建子进程的。
具体来说,fork函数会创建一个新的进程控制块(PCB),并将父进程的PCB全部复制到子进程的PCB中,包括代码段、数据段、堆栈等。
由于子进程是父进程的一个副本,所以它们的代码和数据是完全相同的。
并发程序设计【实验目的】:掌握在程序中创建新进程的方法,观察并理解多道程序并发执行的现象。
【实验原理】:fork():建立子进程。
子进程得到父进程地址空间的一个复制。
返回值:成功时,该函数被调用一次,但返回两次,fork()对子进程返回0,对父进程返回子进程标识符(非0值)。
不成功时对父进程返回-1,没有子进程。
【实验内容】:首先分析一下程序运行时其输出结果有哪几种可能性,然后实际调试该程序观察其实际输出情况,比较两者的差异,分析其中的原因。
void main (void){ int x=5;if( fork(()){x+=30;printf (“%d\n”,x);}elseprintf(“%d\n”,x);printf((“%d\n”,x);}【实验要求】:每个同学必须独立完成本实验、提交实验报告、源程序和可执行程序。
实验报告中必须包含预计的实验结果,关键代码的分析,调试记录,实际的实验结果,实验结果分析等内容。
一.源程序1.1程序.#include<stdio.h>#include<sys/types.h>//pid_t类型的定义#include<unistd.h>//函数fork().getpid()定义void main (void){int x=5;if( fork( ) ){x+=30;printf ("%d\n",x);}elseprintf("%d\n",x);printf("%d\n",x);}1.2预测结果:(1)553535(2)353555(3)535535(4)535355(5)355355(6)355535(7)35351.3实际结果:administrator@ubuntu:~/yanhong$ cc 1.cadministrator@ubuntu:~/yanhong$ ./a.out353555administrator@ubuntu:~/yanhong$ cc 1.cadministrator@ubuntu:~/yanhong$ ./a.out5535351.4结果分析:结果表明,子进程先执行还是父进程先执行是不确定的。
linux核心函数Linux 内核是操作系统的核心部分,它提供了操作系统的核心功能,包括进程管理、内存管理、文件系统等。
Linux 内核的源代码中包含了大量的函数,用于实现各种操作系统的功能。
以下是一些Linux 内核中常见的核心函数,它们扮演着关键的角色:1.进程管理函数:–fork():创建一个新的进程。
–exec():在当前进程中执行一个新的程序。
–wait():等待子进程结束。
–exit():终止当前进程。
2.调度和任务管理函数:–schedule():进行进程调度。
–yield():主动让出CPU,将当前进程移动到就绪队列的末尾。
–wake_up_process():唤醒一个等待中的进程。
3.内存管理函数:–kmalloc():在内核中分配内存。
–kfree():释放内核中的内存。
–vmalloc():在虚拟地址空间中分配内存。
4.文件系统函数:–open():打开一个文件。
–read():从文件中读取数据。
–write():向文件中写入数据。
–close():关闭文件。
5.设备驱动函数:–register_chrdev():注册字符设备。
–unregister_chrdev():注销字符设备。
–request_irq():注册中断处理函数。
6.网络函数:–socket():创建套接字。
–bind():将套接字与地址绑定。
–listen():侦听传入连接请求。
–accept():接受传入的连接请求。
7.定时器和时钟函数:–timer_create():创建一个定时器。
–timer_settime():设置定时器的时间。
–gettimeofday():获取当前时间。
8.同步和互斥函数:–spin_lock():获取自旋锁。
–spin_unlock():释放自旋锁。
–mutex_lock():获取互斥锁。
–mutex_unlock():释放互斥锁。
这些函数仅仅是Linux 内核中众多函数的一小部分,Linux 内核的源代码非常庞大而复杂,包含了各种各样的功能和模块。
本文由我司收集整编,推荐下载,如有疑问,请与我司联系Linux网络编程fork() 和exec() 函数实例分析2014/05/14 1960 Linux进程在内存里有三部分数据:代码段,堆栈段,数据段。
相应的CPU都有上述三种寄存器,以方便操作系统运行,这三部分也是构成一个完整执行序列的必要部分:
代码段-存放程序代码的数据
堆栈段-存储子程序返回地址、子程序参数以及程序局部变量
数据段-存放程序全局变量,常数以及动态数据分配的数据空间
Linux环境下,有两个基本的操作用于创建和修改进程,函数fork()创建一个新的进程,是当前进程的一个拷贝,函数族exec()用来启动另外的进程以取代当前运行的进程,下面通过几个程序来理解fork()和exec()函数的使用方法。
程序fork_test.cpp展示了fork()函数的经典之处,即调用一次,返回两次,该函数执行有三种类型的返回值:在其创建的子进程中为0(子进程仅有一个父进程,可通过函数getppid()获取其父进程pid),在父进程中为子进程的ID(因为一个父进程可能有很多子进程,在父进程中返回子进程ID号便于其跟踪所有子进程),出错则返回-1,对于出错的情况即进程终止运行。
#include unistd.h #include iostream using namespace std;int main(int argc, char **argv) pid_t fpid; fpid = fork(); if(fpid 0) cout “Fork Error!” endl; else if(fpid == 0) cout “The child pid is “ getpid() endl; // cout “The Parent pid is “ getppid() endl; // 可获取其父进程pid else cout “The parent pid is “ getpid() endl; //cout “the child pid is “ fpid endl; //跟踪子进程ID,返回为Child pid return 0;
结果:
The parent pid is 3016。