跳到主要內容

記憶體管理-exit 和 wait

假設處理序 P 有子處理序 A。而 A 使用 exit(),那麼 MM 將會:

  1. 告訴 FS:A 退出,請作相應處理
  2. 釋放 A 佔用的記憶體
  3. 判斷 P 是否正在 WAITING
    • 如果是
      • 清除 P 的 WAITING 位
      • 向 P 發送訊息以解除阻塞(到此 P 的 wait() 函數結束)
      • 釋放 A 的處理序表項(到此 A 的 exit() 函數結束)
    • 如果否
      • 設定 A 的 HANGING 位
  4. 瀏覽 proc_table[],如果發現 A 有子處理序 B,那麼
    • 將 Init 處理序設定為 B 的父處理序(換言之,將 B 過繼給 Init)
    • 判斷是否滿足 Init 正在 WAITING 且 B 正在 HANGING
      • 如果是
        • 清除 Init 的 WAITING 位
        • 向 Init 發送訊息以解除阻塞(到此 Init 的 wait() 函數結束)
        • 釋放 B 的處理序表項(到此 B 的 exit() 函數結束)
      • 如果否
        • 如果 Init 正在 WAITING 但 B 並沒有 HANGING,那麼「握手」會在將來 B 使用 exit() 時發生
        • 如果 B 正在 HANGING 但 Init 並沒有 WAITING,那麼「握手」會在將來 Init 使用 wait() 時發生
如果 P 使用 wait(),那麼 MM 將會:
  1. 瀏覽 proc_table[],如果發現 A 是 P 的子處理序,並且它正在 HANGING,那麼
    • 向 P 發送訊息以解除阻塞(到此 P 的 wait() 函數結束)
    • 釋放 A 的處理序表項(到此 A 的 exit() 函數結束)
  2. 如果 P 的子處理序沒有一個再 HANGING,則
    • 設 P 的 WAITING 位
  3. 如果 P 壓根兒沒有子處理序,則
    • 向 P 發送訊息,訊息攜帶一個表示出錯的返回值(到此 P 的 wait() 函數結束)

程式碼

kernel/main.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*****************************************************************************
 *                               kernel_main
 *****************************************************************************/
/**
 * jmp from kernel.asm::_start.
 *
 *****************************************************************************/
PUBLIC int kernel_main()
{
 disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
   "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");

 int i, j, eflags, prio;
        u8  rpl;
        u8  priv; /* privilege */

 struct task * t;
 struct proc * p = proc_table;

 char * stk = task_stack + STACK_SIZE_TOTAL;

 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++,t++) {
  if (i >= NR_TASKS + NR_NATIVE_PROCS) {
   p->p_flags = FREE_SLOT;
   continue;
  }

         if (i < NR_TASKS) {     /* TASK */
                        t = task_table + i;
                        priv = PRIVILEGE_TASK;
                        rpl     = RPL_TASK;
                        eflags  = 0x1202;/* IF=1, IOPL=1, bit 2 is always 1 */
   prio    = 15;
                }
                else {                  /* USER PROC */
                        t = user_proc_table + (i - NR_TASKS);
                        priv = PRIVILEGE_USER;
                        rpl     = RPL_USER;
                        eflags  = 0x202; /* IF=1, bit 2 is always 1 */
   prio    = 5;
                }

  strcpy(p->name, t->name); /* name of the process */
  p->p_parent = NO_TASK;

  if (strcmp(t->name, "INIT") != 0) {
   p->ldts[INDEX_LDT_C]  = gdt[SELECTOR_KERNEL_CS >> 3];
   p->ldts[INDEX_LDT_RW] = gdt[SELECTOR_KERNEL_DS >> 3];

   /* change the DPLs */
   p->ldts[INDEX_LDT_C].attr1  = DA_C   | priv << 5;
   p->ldts[INDEX_LDT_RW].attr1 = DA_DRW | priv << 5;
  }
  else {  /* INIT process */
   unsigned int k_base;
   unsigned int k_limit;
   int ret = get_kernel_map(&k_base, &k_limit);
   assert(ret == 0);
   init_desc(&p->ldts[INDEX_LDT_C],
      0, /* bytes before the entry point
          * are useless (wasted) for the
          * INIT process, doesn't matter
          */
      (k_base + k_limit) >> LIMIT_4K_SHIFT,
      DA_32 | DA_LIMIT_4K | DA_C | priv << 5);

   init_desc(&p->ldts[INDEX_LDT_RW],
      0, /* bytes before the entry point
          * are useless (wasted) for the
          * INIT process, doesn't matter
          */
      (k_base + k_limit) >> LIMIT_4K_SHIFT,
      DA_32 | DA_LIMIT_4K | DA_DRW | priv << 5);
  }

  p->regs.cs = INDEX_LDT_C << 3 | SA_TIL | rpl;
  p->regs.ds =
   p->regs.es =
   p->regs.fs =
   p->regs.ss = INDEX_LDT_RW << 3 | SA_TIL | rpl;
  p->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;
  p->regs.eip = (u32)t->initial_eip;
  p->regs.esp = (u32)stk;
  p->regs.eflags = eflags;

  p->ticks = p->priority = prio;

  p->p_flags = 0;
  p->p_msg = 0;
  p->p_recvfrom = NO_TASK;
  p->p_sendto = NO_TASK;
  p->has_int_msg = 0;
  p->q_sending = 0;
  p->next_sending = 0;

  for (j = 0; j < NR_FILES; j++)
   p->filp[j] = 0;

  stk -= t->stacksize;
 }

 k_reenter = 0;
 ticks = 0;

 p_proc_ready = proc_table;

 init_clock();
        init_keyboard();

 restart();

 while(1){}
}


/*****************************************************************************
 *                                get_ticks
 *****************************************************************************/
PUBLIC int get_ticks()
{
 MESSAGE msg;
 reset_msg(&msg);
 msg.type = GET_TICKS;
 send_recv(BOTH, TASK_SYS, &msg);
 return msg.RETVAL;
}


/*****************************************************************************
 *                                Init
 *****************************************************************************/
/**
 * The hen.
 *
 *****************************************************************************/
void Init()
{
 int fd_stdin  = open("/dev_tty0", O_RDWR);
 assert(fd_stdin  == 0);
 int fd_stdout = open("/dev_tty0", O_RDWR);
 assert(fd_stdout == 1);

 printf("Init() is running ...\n");

 int pid = fork();
 if (pid != 0) { /* parent process */
  printf("parent is running, child pid:%d\n", pid);
  int s;
  int child = wait(&s);
  printf("child (%d) exited with status: %d.\n", child, s);
 }
 else { /* child process */
  printf("child is running, pid:%d\n", getpid());
  exit(123);
 }

 while (1) {
  int s;
  int child = wait(&s);
  printf("child (%d) exited with status: %d.\n", child, s);
 }
}


/*======================================================================*
                               TestA
 *======================================================================*/
void TestA()
{
 for(;;);
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestB()
{
 for(;;);
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestC()
{
 for(;;);
}

/*****************************************************************************
 *                                panic
 *****************************************************************************/
PUBLIC void panic(const char *fmt, ...)
{
 int i;
 char buf[256];

 /* 4 is the size of fmt in the stack */
 va_list arg = (va_list)((char*)&fmt + 4);

 i = vsprintf(buf, fmt, arg);

 printl("%c !!panic!! %s", MAG_CH_PANIC, buf);

 /* should never arrive here */
 __asm__ __volatile__("ud2");
}

mm/forkexit.c

/*************************************************************************//**
 *****************************************************************************
 * @file   forkexit.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue May  6 00:37:15 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


PRIVATE void cleanup(struct proc * proc);

/*****************************************************************************
 *                                do_fork
 *****************************************************************************/
/**
 * Perform the fork() syscall.
 *
 * @return  Zero if success, otherwise -1.
 *****************************************************************************/
PUBLIC int do_fork()
{
 /* find a free slot in proc_table */
 struct proc* p = proc_table;
 int i;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++)
  if (p->p_flags == FREE_SLOT)
   break;

 int child_pid = i;
 assert(p == &proc_table[child_pid]);
 assert(child_pid >= NR_TASKS + NR_NATIVE_PROCS);
 if (i == NR_TASKS + NR_PROCS) /* no free slot */
  return -1;
 assert(i < NR_TASKS + NR_PROCS);

 /* duplicate the process table */
 int pid = mm_msg.source;
 u16 child_ldt_sel = p->ldt_sel;
 *p = proc_table[pid];
 p->ldt_sel = child_ldt_sel;
 p->p_parent = pid;
 sprintf(p->name, "%s_%d", proc_table[pid].name, child_pid);

 /* duplicate the process: T, D & S */
 struct descriptor * ppd;

 /* Text segment */
 ppd = &proc_table[pid].ldts[INDEX_LDT_C];
 /* base of T-seg, in bytes */
 int caller_T_base  = reassembly(ppd->base_high, 24,
     ppd->base_mid,  16,
     ppd->base_low);
 /* limit of T-seg, in 1 or 4096 bytes,
    depending on the G bit of descriptor */
 int caller_T_limit = reassembly(0, 0,
     (ppd->limit_high_attr2 & 0xF), 16,
     ppd->limit_low);
 /* size of T-seg, in bytes */
 int caller_T_size  = ((caller_T_limit + 1) *
         ((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
          4096 : 1));

 /* Data & Stack segments */
 ppd = &proc_table[pid].ldts[INDEX_LDT_RW];
 /* base of D&S-seg, in bytes */
 int caller_D_S_base  = reassembly(ppd->base_high, 24,
       ppd->base_mid,  16,
       ppd->base_low);
 /* limit of D&S-seg, in 1 or 4096 bytes,
    depending on the G bit of descriptor */
 int caller_D_S_limit = reassembly((ppd->limit_high_attr2 & 0xF), 16,
       0, 0,
       ppd->limit_low);
 /* size of D&S-seg, in bytes */
 int caller_D_S_size  = ((caller_T_limit + 1) *
    ((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
     4096 : 1));

 /* we don't separate T, D & S segments, so we have: */
 assert((caller_T_base  == caller_D_S_base ) &&
        (caller_T_limit == caller_D_S_limit) &&
        (caller_T_size  == caller_D_S_size ));

 /* base of child proc, T, D & S segments share the same space,
    so we allocate memory just once */
 int child_base = alloc_mem(child_pid, caller_T_size);
 /* int child_limit = caller_T_limit; */
 printl("{MM} 0x%x <- 0x%x (0x%x bytes)\n",
        child_base, caller_T_base, caller_T_size);
 /* child is a copy of the parent */
 phys_copy((void*)child_base, (void*)caller_T_base, caller_T_size);

 /* child's LDT */
 init_desc(&p->ldts[INDEX_LDT_C],
    child_base,
    (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
    DA_LIMIT_4K | DA_32 | DA_C | PRIVILEGE_USER << 5);
 init_desc(&p->ldts[INDEX_LDT_RW],
    child_base,
    (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
    DA_LIMIT_4K | DA_32 | DA_DRW | PRIVILEGE_USER << 5);

 /* tell FS, see fs_fork() */
 MESSAGE msg2fs;
 msg2fs.type = FORK;
 msg2fs.PID = child_pid;
 send_recv(BOTH, TASK_FS, &msg2fs);

 /* child PID will be returned to the parent proc */
 mm_msg.PID = child_pid;

 /* birth of the child */
 MESSAGE m;
 m.type = SYSCALL_RET;
 m.RETVAL = 0;
 m.PID = 0;
 send_recv(SEND, child_pid, &m);

 return 0;
}

/*****************************************************************************
 *                                do_exit
 *****************************************************************************/
/**
 * Perform the exit() syscall.
 *
 * If proc A calls exit(), then MM will do the following in this routine:
 *     <1> inform FS so that the fd-related things will be cleaned up
 *     <2> free A's memory
 *     <3> set A.exit_status, which is for the parent
 *     <4> depends on parent's status. if parent (say P) is:
 *           (1) WAITING
 *                 - clean P's WAITING bit, and
 *                 - send P a message to unblock it
 *                 - release A's proc_table[] slot
 *           (2) not WAITING
 *                 - set A's HANGING bit
 *     <5> iterate proc_table[], if proc B is found as A's child, then:
 *           (1) make INIT the new parent of B, and
 *           (2) if INIT is WAITING and B is HANGING, then:
 *                 - clean INIT's WAITING bit, and
 *                 - send INIT a message to unblock it
 *                 - release B's proc_table[] slot
 *               else
 *                 if INIT is WAITING but B is not HANGING, then
 *                     - B will call exit()
 *                 if B is HANGING but INIT is not WAITING, then
 *                     - INIT will call wait()
 *
 * TERMs:
 *     - HANGING: everything except the proc_table entry has been cleaned up.
 *     - WAITING: a proc has at least one child, and it is waiting for the
 *                child(ren) to exit()
 *     - zombie: say P has a child A, A will become a zombie if
 *         - A exit(), and
 *         - P does not wait(), neither does it exit(). that is to say, P just
 *           keeps running without terminating itself or its child
 *
 * @param status  Exiting status for parent.
 *
 *****************************************************************************/
PUBLIC void do_exit(int status)
{
 int i;
 int pid = mm_msg.source; /* PID of caller */
 int parent_pid = proc_table[pid].p_parent;
 struct proc * p = &proc_table[pid];

 /* tell FS, see fs_exit() */
 MESSAGE msg2fs;
 msg2fs.type = EXIT;
 msg2fs.PID = pid;
 send_recv(BOTH, TASK_FS, &msg2fs);

 free_mem(pid);

 p->exit_status = status;

 if (proc_table[parent_pid].p_flags & WAITING) { /* parent is waiting */
  proc_table[parent_pid].p_flags &= ~WAITING;
  cleanup(&proc_table[pid]);
 }
 else { /* parent is not waiting */
  proc_table[pid].p_flags |= HANGING;
 }

 /* if the proc has any child, make INIT the new parent */
 for (i = 0; i < NR_TASKS + NR_PROCS; i++) {
  if (proc_table[i].p_parent == pid) { /* is a child */
   proc_table[i].p_parent = INIT;
   if ((proc_table[INIT].p_flags & WAITING) &&
       (proc_table[i].p_flags & HANGING)) {
    proc_table[INIT].p_flags &= ~WAITING;
    cleanup(&proc_table[i]);
   }
  }
 }
}

/*****************************************************************************
 *                                cleanup
 *****************************************************************************/
/**
 * Do the last jobs to clean up a proc thoroughly:
 *     - Send proc's parent a message to unblock it, and
 *     - release proc's proc_table[] entry
 *
 * @param proc  Process to clean up.
 *****************************************************************************/
PRIVATE void cleanup(struct proc * proc)
{
 MESSAGE msg2parent;
 msg2parent.type = SYSCALL_RET;
 msg2parent.PID = proc2pid(proc);
 msg2parent.STATUS = proc->exit_status;
 send_recv(SEND, proc->p_parent, &msg2parent);

 proc->p_flags = FREE_SLOT;
}

/*****************************************************************************
 *                                do_wait
 *****************************************************************************/
/**
 * Perform the wait() syscall.
 *
 * If proc P calls wait(), then MM will do the following in this routine:
 *     <1> iterate proc_table[],
 *         if proc A is found as P's child and it is HANGING
 *           - reply to P (cleanup() will send P a messageto unblock it)
 *           - release A's proc_table[] entry
 *           - return (MM will go on with the next message loop)
 *     <2> if no child of P is HANGING
 *           - set P's WAITING bit
 *     <3> if P has no child at all
 *           - reply to P with error
 *     <4> return (MM will go on with the next message loop)
 *
 *****************************************************************************/
PUBLIC void do_wait()
{
 int pid = mm_msg.source;

 int i;
 int children = 0;
 struct proc* p_proc = proc_table;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  if (p_proc->p_parent == pid) {
   children++;
   if (p_proc->p_flags & HANGING) {
    cleanup(p_proc);
    return;
   }
  }
 }

 if (children) {
  /* has children, but no child is HANGING */
  proc_table[pid].p_flags |= WAITING;
 }
 else {
  /* no child at all */
  MESSAGE msg;
  msg.type = SYSCALL_RET;
  msg.PID = NO_TASK;
  send_recv(SEND, pid, &msg);
 }
}

Makefile

#########################
# Makefile for Orange'S #
#########################

# Entry point of Orange'S
# It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
ENTRYPOINT = 0x1000

# Offset of entry point in kernel file
# It depends on ENTRYPOINT
ENTRYOFFSET =   0x400

# Programs, flags, etc.
ASM  = nasm
DASM = objdump
CC  = gcc
LD  = ld
ASMBFLAGS = -I boot/include/
ASMKFLAGS = -I include/ -I include/sys/ -f elf
CFLAGS  = -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector
LDFLAGS  = -s -melf_i386 -Ttext $(ENTRYPOINT) -Map krnl.map
DASMFLAGS = -D

# This Program
ORANGESBOOT = boot/boot.bin boot/loader.bin
ORANGESKERNEL = kernel.bin
OBJS  = kernel/kernel.o lib/syscall.o kernel/start.o kernel/main.o\
   kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\
   kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
   kernel/systask.o kernel/hd.o\
   lib/printf.o lib/vsprintf.o\
   lib/kliba.o lib/klib.o lib/string.o lib/misc.o\
   lib/open.o lib/read.o lib/write.o lib/close.o lib/unlink.o\
   lib/getpid.o lib/syslog.o\
   fs/main.o fs/open.o fs/misc.o fs/read_write.o\
   lib/fork.o lib/exit.o lib/wait.o\
   mm/main.o mm/forkexit.o\
   fs/link.o\
   fs/disklog.o
DASMOUTPUT = kernel.bin.asm

# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg

# Default starting position
nop :
 @echo "why not \`make image' huh? :)"

everything : $(ORANGESBOOT) $(ORANGESKERNEL)

all : realclean everything

image : realclean everything clean buildimg

clean :
 rm -f $(OBJS)

realclean :
 rm -f $(OBJS) $(ORANGESBOOT) $(ORANGESKERNEL)

disasm :
 $(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)

# We assume that "a.img" exists in current folder
buildimg :
 mkdir tmp
 sleep 1
 sudo mount -o loop a.img tmp
 sleep 1
 sudo cp -fv boot/loader.bin tmp
 sudo cp -fv kernel.bin tmp
 sleep 1
 sudo umount tmp
 sleep 1
 rmdir tmp

boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

$(ORANGESKERNEL) : $(OBJS)
 $(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $(OBJS)

kernel/kernel.o : kernel/kernel.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/syscall.o : lib/syscall.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

kernel/start.o: kernel/start.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/main.o: kernel/main.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/clock.o: kernel/clock.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/keyboard.o: kernel/keyboard.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/tty.o: kernel/tty.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/console.o: kernel/console.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/i8259.o: kernel/i8259.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/global.o: kernel/global.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/protect.o: kernel/protect.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/proc.o: kernel/proc.c
 $(CC) $(CFLAGS) -o $@ $<

lib/printf.o: lib/printf.c
 $(CC) $(CFLAGS) -o $@ $<

lib/vsprintf.o: lib/vsprintf.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/systask.o: kernel/systask.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/hd.o: kernel/hd.c
 $(CC) $(CFLAGS) -o $@ $<

lib/klib.o: lib/klib.c
 $(CC) $(CFLAGS) -o $@ $<

lib/misc.o: lib/misc.c
 $(CC) $(CFLAGS) -o $@ $<

lib/kliba.o : lib/kliba.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/string.o : lib/string.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/open.o: lib/open.c
 $(CC) $(CFLAGS) -o $@ $<

lib/read.o: lib/read.c
 $(CC) $(CFLAGS) -o $@ $<

lib/write.o: lib/write.c
 $(CC) $(CFLAGS) -o $@ $<

lib/close.o: lib/close.c
 $(CC) $(CFLAGS) -o $@ $<

lib/unlink.o: lib/unlink.c
 $(CC) $(CFLAGS) -o $@ $<

lib/getpid.o: lib/getpid.c
 $(CC) $(CFLAGS) -o $@ $<

lib/syslog.o: lib/syslog.c
 $(CC) $(CFLAGS) -o $@ $<

lib/fork.o: lib/fork.c
 $(CC) $(CFLAGS) -o $@ $<

lib/exit.o: lib/exit.c
 $(CC) $(CFLAGS) -o $@ $<

lib/wait.o: lib/wait.c
 $(CC) $(CFLAGS) -o $@ $<

mm/main.o: mm/main.c
 $(CC) $(CFLAGS) -o $@ $<

mm/forkexit.o: mm/forkexit.c
 $(CC) $(CFLAGS) -o $@ $<

fs/main.o: fs/main.c
 $(CC) $(CFLAGS) -o $@ $<

fs/open.o: fs/open.c
 $(CC) $(CFLAGS) -o $@ $<

fs/read_write.o: fs/read_write.c
 $(CC) $(CFLAGS) -o $@ $<

fs/link.o: fs/link.c
 $(CC) $(CFLAGS) -o $@ $<

fs/disklog.o: fs/disklog.c
 $(CC) $(CFLAGS) -o $@ $<
執行畫面

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...