「源码分享」ASP.NET仓库进销存ERP管理系统+小程序源码

「源码分享」ASP.NET仓库进销存ERP管理系统+小程序源码,第1张

开发环境为Visual Studio 2012,数据库为SQL SERVER2012R2,使用.net 4.5开发。

一、源码介绍

所有行业的ERP系统/进销存/仓库系统,该系统为vs2012 .net+MsSQL版,目前标准版功能简单、明了、满足公司正常谨携使用,已有多家企业正常使用,成熟稳定,有需要的可以下载看看。

二、主要功能

1、电商管理(可以和公众号、小程序对接) 微信订单、小程序订单、公众号订单

参数设置:轮播图片设置、分类导航设置、小程序参数设置、公众号参数设置

2、销售管理:销售订单、销售出库

3、采购管理:采购订单、采购入库

4、生产管理:BOM分组、BOM清单、生产计划、生产领料、生产入库

5、仓库管理:其他入库、其他出库、商品组装、商品拆卸、库存盘点、库存调拨

6、财务管理:销售收款、其他收款、采购付款、其他付款、收款核销、付款核销

7、采购报表

采购订单跟踪表 采购明细表 采购汇总表(按商品) 采购汇总表(按供应商)

8、销售报表

销售订单跟踪表 销售明细表凳宽 销售汇总表(按商品) 销售汇总表(按客户)

9、生产报表

生产计划跟踪表 生产领料明细表 生产领料汇总表 生产入库明细表 生产入库汇总表

10、仓存报表

商品库存余额表 商品收发明细表 商品收发汇总表

11、资金报表

现金银行报表 应付账款明细表 应收账款明细表

客户对账单 供应商对账单 其他收支明细

12、基础资料

供应商管理 商品管理 仓库管祥粗伏理 账户管理 员工管理 企业号通讯录

工序管理 辅助资料 客户类别 供应商类别 商品类别 商品品牌

收支类别 计量单位 结算方式 工序类别 高级设置 *** 作日志

参数设置 打印设置 Logo印章

3、默认数据库连接字符串在web.config配置文件中修改

获取源码方式:点赞+转发+关注+私信【进销存ERP】

******************************************************************************

欢迎点赞+转发+关注!大家的支持是我分享最大的动力!!!

******************************************************************************

Windows 是闭源开发的商业软件,受商业版权法保护,别说大家都没有,就算有也不能给你的(被微软起诉那可不是小事)。

Linux的是开源的,干嘛不去读Linux源码呢?

新版本的太长,写不开,给你最经典的0.11版的进程管理源码。其他的你自己去查。作为一个好的程序员,必须学会充分利用搜索引擎。

---------------------------------------

linux0.11通过一个进程数组管理进程,最大允许64个进程存在。进程的状态有就绪态,运行态,暂停态,睡眠态和僵死态等。睡眠态又可分为可中断和不可中断两掘历种。对于进程的管理,我觉得按照进程的状态来讲会清晰一些。

1.0号任务

0号比较特殊,是"纯手工制作",下面就是制作过程

mem_init(main_memory_start,memory_end)

trap_init()

blk_dev_init()

char_dev_init()

tty_init()

time_init()

sched_init()

buffer_init(buffer_memory_end)

hd_init()

floppy_init()

sti()

move_to_user_mode()

这么多init当然不全是为0任务准备的,他们是初始化系统。对任务0来说,重要的是sched_init()和move_to_user_mode()两个。sched_init()中手动设置了任务0的任务段描述符tss和局部段描述符ldt,并设置了tr和ldtr寄存器:

set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss))//设置tss段描述符

set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt))//设置ldt描述符

...

ltr(0) //加载tss描述符地址到tr

lldt(0) //加载ldt描述符地址到ldtr

我们来看一下任务0的tss和ldt是什么样子

/*ldt*/ {0,0},\

{0x9f,0xc0fa00},\

{0x9f,0xc0f200},\

/*tss*/{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\

0,0,0,0,0,0,0,0,\

0,0,0x17,0x17,0x17,0x17,0x17,0x17,\

_LDT(0),0X80000000,\

{}\

},\

从ldt 可以看到,任务的代码和数据段均为640k,基址为0,DPL=3。这说明虽然任务0的代码还是处在内核段内,但是任务的级别已经是用户态了。从tss也可以看到这一点,代码和数据都被置为0x17,也就是局部段描述符表第2项。还可以看到任务0的内核太堆栈被设置在init_task之后的一页处,用户态堆栈就是move_to_user_mode之前的内核态堆栈。这和普通进程不一样,普通进程晌散迅的用户态堆栈宴此在其64Mb地址空间的末端。

move_to_user_mode是在堆栈中创建一个任务切换的假象,用iret跳转到外层3,这样cpu就会自动根据tr加载tss,并初始化各个寄存器运行任务0。所以,任务0其实就是内核空间中的用户态任务。

2.进程的创建

进程创建是由系统调用sys_fork完成的,主要使用了两个函数find_empty_process和copy_process。前者在进程在进程数组中找一个不用的进程号给子进程,后者完成子进程信息的创建,主要是复制父进程的信息。

我们来看一下copy_process的代码:

int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,

long ebx,long ecx,long edx,long fs,long es,long ds,

long eip,long cs,long eflags,long esp,long ss)

{

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

//将子进程的状态设为不可中断等待,防止现在被调度

p->state=TASK_UNINTERRUPTIBLE

P->pid=last_pid

p->father=current->pid

p->count=p->priority

p->signal=0

p->alarm=0

p->leader=0

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

//ip为父进程调用fork的下一条指令

p->tss.eip=eip

//fork的返回值对子进程来说是0,对父进程来说是它的pid,通过这个区别,在fork调用返回后,父子进程的代码段便被分割来,

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

//如果父任务使用了协处理器,还要保存到tss中

if(last_task_used_math==current)

_asm("cltsfnsave %0"::"m"(p->tss.i387))

//为新任务设置新的代码和数据段基地址。注意,因为linux0.11只支持64M的进程空间,所以进程的线性地址空间在64M的边界处。

//然后为新任务复制父进程的页表。通过copy_page_tales,父子任务此时共用一个只读的代码数据段。在写 *** 作时,写时复制会为新进程申请新的物理页面。

if(copy_mem(nr,p)){

task[nr]=NULL

free_page((long)p)

return -EAGAIN

}

for(i=0i<NR_OPENi++)

if(f=p->filp)

f->f_count++

if(current->pwd)

current->pwd->i_count++

if(current->root)

current->root->i_count++

if(current->executable)

current->executable->i_count++

//设置新任务的tss和ldt描述符

set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss))

set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt))

//返回子进程的pid

return last_pid

}

参数都是堆栈中的值,nr是调用copy_process之前的find_empty_process的返回值,作为任务数组的序号。

3.进程的调度

进程创建之后并不是立即执行。系统会在适当的时刻调用系统调度函数schedule,它会选择适当的进程运行。调度函数可能在系统调用结束之后,进程暂停/ 睡眠/退出,时钟中断等多个地方调用。调度函数主要是通过进程的时间片来选择一个运行时间最短的进程运行。如果没有找到就运行空闲pause系统调用,在 Pause中,调度函数又会被调用。下面是主要代码

while(1){

c=-1

next=0

i=NR_TASKS

p=&task[NR_TASKS]

//寻找就绪任务中运行时间最短的任务

while(--i){

if(!(*--p))

continue

if((*p)->state==TASK_RUNNING&&(*p)->counter>c)

c=(*p)->counter,next=i

}

//如果找到,就退出。否则重新计算任务的时间片。注意,如果没有任务,next始终为0,结果就被切换到任务0

if(c)break

for(p=&LAST_TASKp>&FIRST_TASK--p)

if(*p)

(*p)->counter=((*p)->counter>>1)+(*)->priority

}

switch_to(next)//通过长跳转,转换任务到新任务。

}

时钟中断是执行调度的重要措施,正是由于不断的执行调度,系统才能在多个任务之间进行切换,完成多任务 *** 作系统的目标。在调度器初始化时,除了手动设置了任务0,还对8253开启了定时器中断,对系统"点火".中断中调用了do_timer函数。现在这是个很短的函数:

void do_timer(long cpl)

{

...

//根据优先级调整进程运行时间

if(cpl)

current->utime++

else

current->stime++

//处理定时器中断队列

if(next_timer){

next_timer->jiffies--

while(next_timer&&next_timer->jiffies<=0){

void(*fn)(void)

fn=next_timer->fn

next_timer->fn=NULL

next_timer=next_timer->next

(fn)()

}

}

..

//对进程时间片减1。如果大于0 ,表示时间片还没有用完,继续

if((--current->counter>0)return

//注意,内核态任务是不可抢占的,在0.11中,除非内核任务主动放弃处理器,它将一直运行。

if(!cpl)return

//调度

schedule()

}

4.进程的终止

进程可以自己中止,也可以被中止信号中止。do_exit执行退出。如果它有子进程的话,子进程就会成为孤儿进程,这里会将它的孩子托付给进程1(即init进程)管理。在成为僵死进程之后,还要通知父进程,因为他的父进程可能在等它呢。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8230944.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存