跳轉到內容

Aros/開發者/文件/庫/DOS

來自華夏公益教科書
用於 Aros 華夏公益教科書的導航欄
Aros 使用者
Aros 使用者文件
Aros 使用者常見問題解答
Aros 使用者應用程式
Aros 使用者 DOS Shell
Aros/使用者/AmigaLegacy
Aros 開發者文件
Aros 開發者文件
從 AmigaOS/SDL 移植軟體
適用於 Zune 初學者
Zune .MUI 類
適用於 SDL 初學者
Aros 開發者構建系統
特定平臺
Aros x86 完整系統 HCL
Aros x86 音訊/影片支援
Aros x86 網路支援
Aros Intel AMD x86 安裝
Aros 儲存支援 IDE SATA 等
Aros Poseidon USB 支援
x86-64 支援
摩托羅拉 68k Amiga 支援
Linux 和 FreeBSD 支援
Windows Mingw 和 MacOSX 支援
Android 支援
Arm Raspberry Pi 支援
PPC Power Architecture
其他
Aros 公共許可證

檔案系統控制檔案和資料夾在磁碟上的佈局方式,並處理建立、讀取、寫入和刪除這些檔案的所有細節。如果沒有檔案系統,您的磁碟(或軟盤、CD-ROM、USB 金鑰或 SD 卡)只是一堆未結構化的原始資料。

當您訪問檔案時,每次都會建立一個檔案控制代碼。

  • dos.h 包含 Open、Close、Seek、Read、Write 等。
  • stdio.h 包含 fopen、fclose、fseek、fread、fwrite 等。

所有檔案例程都透過 dos.library,即使您使用 fopen(),這些“非原生”庫函式只是用 C 標準化檔案訪問的一些實現,如果您跟蹤程式碼,它們最終會使用 Aros 作業系統呼叫。

malloc 等也是如此,它們最終會進入 exec.library 的 AllocMem/AllocVec/AllocPooled 或其他。

需要注意的是,當您呼叫某個作業系統函式時,您的程式碼實際上是在執行 OS 程式碼,沒有幕後超級程式碼為您執行此操作。您的應用程式 PC 計數器會透過這些呼叫。

如果您使用標準 C 庫呼叫,則必須將其連結到二進位制檔案,或者二進位制檔案必須在執行時開啟 clib。您不希望在編寫作業系統部分時擁有 clib(使用者應用程式可以隨意操作)。如果使用了一個,C 庫呼叫了作業系統呼叫,那麼就會變得很亂(clib 程式碼會將 C 標準轉換為原生呼叫等)。

當您從編譯器的標準 C 庫或 POSIX 型別函式庫中使用通用例程時,在處理 AROS(或 Amiga)系統內容時通常會看到特定的限制。它們對檔案許可權位可能有不相容的想法,對如何處理多個根卷或查詢實際上是“多路徑分配”的東西或使用檔案註釋(Amiga 原生應用程式編寫人員利用了這些註釋)可能有不相容的想法。標準庫不理解 Amiga 工作臺環境中程式和圖示之間的關係。

標準庫程式設計可能意識不到使用者在遇到大量“插入卷 foo:請求者”彈出視窗或更糟的是,在應用程式在其自身螢幕上執行時在工作臺螢幕上彈出視窗,阻塞對這些請求者的檢視,因為它們是用 Unix shell 語義編寫的,其中錯誤處理的方式不同。

堅持使用 AROS 原生函式名稱可能是安全的,即使標準呼叫在內部被重新對映。

分配一大塊記憶體(使用 AvailMem 和標誌 MEMF_LARGEST 獲取大小,並使用 AllocDosObject(以前是 AllocMem)分配,檔案大小使用 Lock 和 Examine(ExAll)獲取),使用 Open 開啟檔案,並使用 Read 讀取資料,使用 Open 開啟原始檔,並使用 Write 儲存資料。對於總 RAM,使用 MEMF_TOTAL。

檢查現有檔案的一種簡單方法是:使用 Examine/ExNext/ExAll 獲取一個目錄的資料,並嘗試對目標目錄中的每個檔名進行 Lock()。要更改目錄/建立目錄,CurrentDir 和 CreateDir 很有用。Rename 和 DeleteFile 函式將很有用。

struct FileHandle
{
    /* The next three are used with packet-based filesystems */
    struct Message * fh_Link;   /* exec message containing packet */
    struct MsgPort * fh_Port;   /* packet reply port */
    struct MsgPort * fh_Type;   /* port to send packets to */

    UBYTE * fh_Buf;
    UBYTE * fh_Pos;
    UBYTE * fh_End;

#ifdef AROS_DOS_PACKETS

    LONG fh_Funcs;
#define fh_Func1 fh_Funcs
    LONG fh_Func2;
    LONG fh_Func3;
    LONG fh_Args;
#define fh_Arg1 fh_Args
    LONG fh_Arg2;

    /* kept here until things stabilize */
    ULONG        fh_Size;
    ULONG        fh_Flags;

#else

    /* The following four fields have different names and a different function than their AmigaOS equivalents. 
       The original names were:      fh_Funcs/fh_Func1, fh_Func2, fh_Func3, fh_Args/fh_Arg1 and fh_Arg2 */
    ULONG        fh_Size;
    ULONG        fh_Flags;   /* see below */
    /* This is a pointer to a filesystem handler. See <dos/filesystems.h> for more information. */
    struct Device * fh_Device;

    /* SDuvan: Added this and removed the #if below. This field allows us to emulate packets -- 
               specifically it makes it possible to implement the ***Pkt() functions */
    SIPTR fh_CompatibilityHack;

    /* A private pointer to a device specific filehandle structure. See <dos/filesystems.h> for more information. */
    struct Unit   * fh_Unit;

#endif
};

外部程式碼引用的唯一欄位是 fh_Device 和 fh_Unit。它們用於傳送直接 IOFS 請求。實際上,fh_Size 和 fh_Flags 也可以別名為 fh_Funcs 和 fh_Func2,但這會影響 m68k 埠,如果它們決定出於某種原因實現這些回撥。如果可能會儲存指標的欄位升級為 SIPR 而不是 APTR,可能會更好。這將與 AmigaOS 程式碼提供更好的原始碼級別相容性,例如避免編譯器警告。

以前的實現(對於非 AROS_DOS_PACKET 情況,欄位 fh_Size 在偏移量 24,欄位 fh_Flags 在偏移量 28)。更改後,欄位 fh_Size 在偏移量 44,欄位 fh_Flags 在偏移量 48。上面是更改前後的 FH 結構,供參考。

Info() 函式獲取系統中卷的資訊。

  • lock—對捲上任何應提供資訊的鎖定檔案
  • parameterBlock—指向 InfoData 結構的指標

結果為 != 0(如果操作成功)或 == 0(如果操作不成功)。如果成功(!= 0),則“parameterBlock”將填充有關卷的資訊。

/* 由 Info() 返回,必須位於 4 位元組邊界上 */ struct InfoData {

  LONG	  id_NumSoftErrors;	/* number of soft errors on disk */
  LONG	  id_UnitNumber;	/* Which unit disk is (was) mounted on (os1.3 only) */
  LONG	  id_DiskState;		/* ID_WRITE_PROTECTED, ID_VALIDATING, ID_VALIDATED */
  LONG	  id_NumBlocks;		/* Number of blocks on disk */
  LONG	  id_NumBlocksUsed;	/* Number of block in use */
  LONG	  id_BytesPerBlock;
  LONG	  id_DiskType;		/* (ID_NO_DISK_PRESENT, ID_UNREADABLE_DISK, ID_NOT_REALLY_DOS, ID_BUSY)
                                   Filesystem ID_DOS_DISK, ID_FFS_DISK, ID_INTER_DOS_DISK, ID_INTER_FFS_DISK, 
                                              ID_FASTDIR_DOS_DISK, ID_FASTDIR_FFS_DISK, 
                                              ID_LONGNAME_DOS_DISK, ID_LONGNAME_FFS_DISK, ID_MSDOS_DISK 
                                   Console    ID_CON, ID_RAWCON, ID_KICKSTART_DISK */
  BPTR	  id_VolumeNode;	/* BCPL pointer to volume node */
  LONG	  id_InUse;		/* Flag, zero if not in use */

}; /* InfoData */

要查詢 Shell 執行的 CON:/RAW: 視窗的地址;AmigaDOS 手冊中對這一點的記錄很少,但事實是,如果將 DiskInfo 資料包傳送到控制檯處理程式,InfoData 結構中的 id_VolumeNode 欄位將儲存所需的視窗地址。

#include	
#include	
#include	
extern struct Library	*OpenLibrary();
struct Library	*IntuitionBase;
struct Window	*wb_window;

   wb_window = NULL;
#if	RAW_CONSOLE
   if ((IntuitionBase = OpenLibrary("intuition.library", 0L)) != NULL) {
	BPTR			iLock;
	struct FileHandle	*conF;
	struct InfoData		*infTemp;
	extern long		dos_packet();

	iLock = (BPTR) Input();
	conF = (struct FileHandle *) BADDR(iLock);
	infTemp = (struct InfoData *) malloc(sizeof(struct InfoData));
	if (infTemp == 0)
		return;
	/*
	 * Send the packet.
	*/
	if (!dos_packet(conF->fh_Type, ACTION_DISK_INFO, 
			((ULONG) infTemp) >> 2)) {
		free((void *) infTemp);
		return;
	}
	wb_window = (struct Window *) infTemp->id_VolumeNode;
   }
#endif

開啟關閉檔案

[編輯 | 編輯原始碼]

在您可以對檔案執行任何操作之前,您必須要求作業系統“開啟它”。這是因為 AROS 需要知道您要對檔案做什麼。您是要建立一個新檔案(MODE_NEWFILE),還是要使用已開啟的檔案(MODE_OLDFILE)。您可以透過呼叫 Open() 函式開啟檔案。

例如,如果您要建立一個名為“Highscore.dat”的新檔案,則可以編寫以下程式碼:

/* Try to open the file: */
file_handle = Open("Highscore.dat", MODE_NEWFILE );

/* Check if we have opened the file: */
if( file_handle == NULL )
  /* Could NOT open file! */

完成讀取/寫入檔案後,您需要關閉它。您可以透過呼叫 Close() 函式來完成。

Close(file_handle);

關閉所有已開啟的檔案!

/*
    Copyright © 1995-2010, The AROS Development Team. All rights reserved.
    $Id: filetest.c 37364 2011-03-04 22:40:55Z jmcmullan $
*/

#include <dos/dos.h>
#include <stdio.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>

static void hexdump(UBYTE *data, LONG len)
{
    LONG i;

    for (i = 0; i < len; i++)
        printf("%02X ", data[i]);

    printf("\n");
}

int main(int argc, char **argv)
{
    BPTR file;
    UBYTE buffer[20];
    LONG r1=0,r2=0,r3=0,r4=0;
    char *name = "testfile";
    
    if (argc > 1)
	name = argv[1];

    file=Open(name, MODE_NEWFILE);
    if(file)
    {
	r1=Write(file,"hello, world\n",13);
	r2=Seek(file,0,OFFSET_BEGINNING);
	r3=Read(file,buffer,19);
	r4=Close(file);
    }
    if(r3>=0)
	buffer[r3]=0;

    printf("Results: %d %d %d %d \'%s\'\n",(int)r1,(int)r2,(int)r3,(int)r4,buffer);
    hexdump(buffer, r3);
    
    return 0;
}

使用任何全域性 FILE 指標都會帶來麻煩,因此最好不要使用它們。只需在每個函式解析時宣告其用法(如果需要)。全域性變數是一個糟糕的主意,如果您養成不加思考就使用它們的習慣,將來可能會遇到麻煩。在繼續之前,請確保您瞭解變數作用域及其後果。

讀取、寫入和查詢檔案

[編輯 | 編輯原始碼]

成功開啟檔案後,就可以開始讀取或寫入。AmigaDOS(TM) 和 AROS 將檔案視為位元組流,當您讀取或寫入內容時,您需要指定要讀取或寫入的位元組數。

要寫入檔案,可以使用 Write() 函式

要查詢

#include <stdio.h>
#include <dos/dos.h>
#include <stdlib.h>

#include <proto/dos.h>

int main()
{
FILE *fd;
char buffer[32];
int i;
BPTR file;

  fd = fopen( "seek.txt", "wb" );
  if ( !fd )
  {
    fprintf( stderr, "Could not write test file seek.txt\n" );
    exit(-1);
  }
  fprintf( fd, "() does not work!\n" );
  fclose(fd);

/* fseek() */
  fd = fopen( "seek.txt", "rb" );
  if ( !fd )
  {
    fprintf( stderr, "Could not open test file seek.txt\n" );
    exit(-1);
  }
  i = fread( buffer, 1, 1, fd );
//printf("pos=%ld\n",ftell(fd));
  i += fread( &buffer[1], 1, 6, fd );
  if( i != 7 )
  {
    fprintf( stderr, "Wanted to fread() %d chars, but could only get %d!\n", 6, i-1 );
    exit(-1);
  }
  fseek( fd, 4, SEEK_CUR );
  i = fread( &buffer[7], 1, 11, fd );
  buffer[7+i]=0;
  printf( "fseek%s", buffer );
  fclose(fd);

/* Seek() */
  file = Open( "seek.txt", MODE_OLDFILE );
  i = Read( file, buffer, 7 );
  Seek( file, 4, OFFSET_CURRENT );
  i += Read( file, &buffer[7], 11 );
  buffer[i] = 0;
  printf( "\nSeek%s", buffer );
  
  return 0;
}

另請參閱 此處

#include "dos/dosextens.h"
extern struct FileHandle *Open()

main()
{
    struct FileHandle *file_handle;
    file_handle = Open("CON:10/10/500/150/New Window", MODE_NEWFILE);

    Write (file_handle,"Hello World\n",13);
    Delay(400);
    Close(file_handle);
}
currentposition=Seek(file_handle,0, OFFSET_END); 
currentposition=Seek(file_handle,0, OFFSET_CURRENT); 
currentposition=Seek(file_handle,20, OFFSET_CURRENT); 
currentposition=Seek(file_handle,0, OFFSET_BEGINNING);
boolean_status=WaitForChar(file_handle,waiting_time);

flock 使用以 MODE_NEWFILE 模式開啟的檔案控制代碼呼叫 DupLockFromFH()。不允許重複它。可以透過用以下內容替換 O_CREAT = NEWFILE 來解決:首先使用 MODE_NEWFILE 建立檔案,關閉它,然後使用 MODE_OLDFILE 重新開啟它。ChangeMode() 也是一種可能,但它可能不受所有檔案系統支援,或者它可能已損壞(直到大約兩年前,UAE fs 實現的 CHANGE_FH 變體一直存在錯誤。它在 AOS 程式中確實很少使用)。

實際上,flock() 呼叫 NameFromFH(),而 NameFromFH() 本身又呼叫 DupLockFromFH()。所以第一個問題是,如果使用 MODE_NEWFILE 開啟的控制代碼,NameFromFH() 是否應該失敗?在 OS3.x 上是否會失敗?NameFromFH() 必須能夠處理使用 MODE_NEWFILE 開啟的檔案控制代碼。Guru Book 中也提到了,AOS 的 NameFromFH() 在內部使用 ExamineFH() 和 ParentOfFH() 來獲取名稱字串,因為如果控制代碼是 MODE_NEWFILE,DupLockFromFH() 無法工作。

看起來它做了類似這樣的事情

  • parentlock = ParentOfFH(fh);
  • 路徑字串 = NameFromLock(parentlock);
  • Unlock(parentlock)
  • 從 ExamineFH(fh) 中獲取檔名並追加到路徑字串

為什麼 DupLockFromFH() 不能在 MODE_NEWFILE 檔案上工作?據我所知,這是因為 MODE_NEWFILE 使用了排它鎖(在所有檔案系統上,不僅僅是在 AmberRAM 上)。

如果我們讓它工作,會有什麼問題嗎?UAE 的檔案系統需要重新編寫,但這只是其中的一部分,對吧?這不會破壞與 AOS m68k 檔案系統的相容性嗎?是的。排它鎖複製(包括具有內部排它鎖的 MODE_NEWFILE 檔案控制代碼)嘗試必須失敗。這是已記錄的行為。NameFromFH() 現在已重寫,可以處理排它檔案控制代碼。

SameLock()

dol_Name 是指向 BCPL 字串的 BCPL 指標,因此你不能直接使用 printf() 列印它。請注意,即使如此,你也應該避免這樣做,尤其是在你持有 dos 列表鎖的時候。最好將你需要的的資訊複製到自己的緩衝區中,然後在解鎖 dos 列表後列印它。

char buffer[256];
const uint8 *bstr = BADDR(dlPtr->dol_Name);
memcpy(buffer, bstr+1, *bstr);
buffer[*bstr] = 0;

你永遠不應該在 dos 列表被鎖定的情況下執行任何 DOS I/O 操作。建立一個包含你想要的操作的執行列表,釋放 dos 列表,然後打印出列表節點......

在 DOS 列表中遍歷,並顯示列表中的所有裝置。如果你想只過濾那些已經被啟用的裝置,你需要編寫一個新程式來遍歷列表(LockDosList / NextDosEntry),並檢查 dol_Task 是否已填充(OS4 將其重新命名為 dol_Port)。名稱是 dol_Task(請參閱 dos/dosextens.h)。它包含指向 MsgPort 的指標。但請注意,這隨時可能發生變化。一個裝置可以被掛載,但可能不會立即被啟用。當用戶首次訪問它時,它會自動被啟用。例如,這適用於 RAM、SER、PAR 和 PRT

卷標

#include <exec/types.h> 
#include <exec/memory.h> 
#include <dos/dos.h> 
#include <clib/exec_protos.h> 
#include <clib/dos_protos.h>

#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h>

struct InfoData *info; 
struct DosList *doslist; 
char *name; 
void main(void) 
{ 
 BPTR lock, mydoslist;
 BOOL success;
 lock=Lock("File",ACCESS_READ);

 if(!lock) exit(20);
 info=(struct InfoData*)AllocVec(sizeof(struct InfoData),MEMF_PUBLIC|MEMF_CLEAR);
 success=Info(lock,info);

 if(!success) exit(30);
 doslist=(struct DosList*)((info->id_VolumeNode)<<2);
 name=((struct Node*)(((struct MsgPort*)(doslist->dol_Task))->mp_SigTask))->ln_Name;
 printf("%s:\n",name);
 FreeVec(info);
}
#include <dos/dosextens.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <stdio.h>

#define MAXV 40
struct DE {
   char name[256];
   APTR task;
};
struct DE vols[MAXV], devs[MAXV];

void ReadDosList(struct DE *list, int *max, int type) {
   struct DosList *dl;
   int i = 0;
   UBYTE *x;
   if (!(dl = LockDosList(type))) {*max = 0; return;}
   while ((dl = NextDosEntry(dl, type))) {
     if (i >= MAXV) continue;
     x = BADDR(dl->dol_Name);
     CopyMem(&x[1], &list[i].name, x[0]);
     list[i].name[x[0]] = 0;
     list[i].task = dl->dol_Task;
     i++;
   }
   UnLockDosList(type);
   *max = i;
}

int main() {
   int i, j, voln, devn;
   STRPTR x;
   SysBase = *((struct ExecBase **)4UL);
   if ((DOSBase = OpenLibrary("dos.library", 37))) {
     ReadDosList(&devs[0], &devn, LDF_DEVICES | LDF_READ);
     ReadDosList(&vols[0], &voln, LDF_VOLUMES | LDF_READ);
     CloseLibrary(DOSBase);
   }
   for (i = 0; i < voln; i++) {
//     x = "o device?;
     for (j = 0; j < devn; j++)
       if (vols[i].task == devs[j].task) x = devs[j].name;
     printf("%s -> %s\n", vols[i].name, x);
   }
   return 0;
}

要將 WBArg 轉換為完整路徑,你可以執行以下操作

TEXT fullpath[1024];

NameFromLock(WBArg->wa_Lock, fullpath, sizeof(fullpath));
AddPart(fullpath, WBArg->wa_Name, sizeof(fullpath));

但是,通常更有效的方法是

BPTR oldcd, file;

oldcd = CurrentDir(WBArg->wa_Lock);
file = Open(WBArg->wa_Name, MODE_OLDFILE);
CurrentDir(oldcd);

if (file != 0) {
    /* ... */
    Close(file);
}

檢查檔案或目錄

[編輯 | 編輯原始碼]

最新的方法需要 ExAll,它取代了 Examine() 和 ExNext()。當 ExAll() 掃描一個目錄(之前已經鎖定)時,它會將一個 ExAllData 結構寫入緩衝區,用於每個條目。

示例 - 查詢

舊的方法需要 Examine()。你向函式傳遞一個指向你要檢查的檔案或目錄的“鎖”的指標,它將為你設定一個 FileInfoBlock 結構。

/*
    Copyright © 1995-2002, The AROS Development Team. All rights reserved.
    $Id: ExNext.c 33409 2010-05-31 13:45:26Z mazze $

    Test ExNext() function
*/

#include <proto/dos.h>
#include <proto/exec.h>

#include <dos/dos.h>
#include <dos/exall.h>
#include <dos/rdargs.h>
#include <dos/var.h>
#include <exec/memory.h>
#include <exec/types.h>

#include <utility/tagitem.h>
#include <stdio.h>

#include <aros/debug.h>

#define ARG_TEMPLATE    "DIRECTORY"
#define TOTAL_ARGS      1

static const char version[] = "$VER: ExNext 41.1 (30.01.2000)\n";

int main(int argc, char *argv[])
{
    struct RDArgs	* rda;
    IPTR		* args[TOTAL_ARGS] = { NULL };
    int			  Return_Value;
    BPTR		  lock;

    Return_Value = RETURN_OK;

    rda = ReadArgs(ARG_TEMPLATE, (IPTR *)args, NULL);
    if (rda)
    {
	if (!args[0])
	    lock = Lock("", ACCESS_READ);
	else
	    lock = Lock((STRPTR)args[0], ACCESS_READ);

	if (lock)
	{
	    struct FileInfoBlock * FIB;
	    BOOL success;
	    FIB = AllocVec(sizeof(struct FileInfoBlock), MEMF_CLEAR);
	    if (FIB)
	    {
	      success = Examine(lock, FIB);
	      kprintf("calling ExNext()...\n");
	      success = ExNext(lock, FIB);
	      kprintf("called ExNext()\n");
	      while (success != DOSFALSE)
	      {
		/* don't show dirs */
		if (FIB->fib_DirEntryType < 0)
		    printf("%s\n",FIB->fib_FileName);
		else
		    printf("%s (not a file)\n", FIB->fib_FileName);
		kprintf("calling ExNext()...\n");
		success = ExNext(lock, FIB);
		kprintf("called ExNext()\n");
	      }
	      FreeVec(FIB);
	    }
	    UnLock(lock);
	}
	else
	{
	    PrintFault(IoErr(), "ExNext");
	    Return_Value = RETURN_FAIL;
	}
    }
    else
    {
        PrintFault(IoErr(), "ExNext");
        Return_Value = RETURN_ERROR;
    }

    if (rda)
        FreeArgs(rda);

    return (Return_Value);
} /* main */

使用 AllocMem 分配的記憶體與 dos 函式 Examine 一起使用,並將 fib_SIZEOF 作為大小是否安全?或者使用 AllocDosObject 是否更好?AllocMem 對所有當前存在的 AmigaOS 版本都是安全的,但 AllocDosObject 對於未來的相容性更好。請注意,AllocDosObject 在 Kickstart 1.3 及以下版本中不存在,因此對於這些 OS 版本,你需要使用 AllocMem。

struct FileInfoBlock fib;
BPTR lock = Lock(filename, ACCESS_READ);

if (lock)
{
   Examine(lock, &fib);
   UnLock(lock);
}

在 PowerPC 上,堆疊始終是長字對齊的,你不必使用 AllocDosObject() 或 AllocMem()。如果你針對 AmigaOS/68k 編碼,你必須分配長字對齊的 FIB。

   TEXT buf[500];
   struct FileInfoBlock *fib = (APTR)&buf;

   stccpy(buf, "PROGDIR:", sizeof(buf));
   AddPart(buf, "Image.png", sizeof(buf));  // Buf is passed to dos.library
   fh = Open(buf, MODE_OLDFILE);            // Again buf is passed to dos.library
   ExamineFH(fh, &fib);                                 // Yet again same buf is passed to dos.library
   Close(fh);

FileInfoBlock 不是系統結構。它只是一個指向用於檢索資料的緩衝區的指標。如果你仍然堅持 FIB 必須使用 AllocDosObject() 分配(為什麼?),你也應該使用系統例程為傳遞給 Lock()、Open() 等的名稱分配空間。

正如你所看到的,這個使用 AllocDosObject() 分配 FIB 的想法不是一個好主意。對於其他結構,例如 ExAllControl,它確實是必要的,因為結構在傳遞給 dos.library 之前必須正確初始化。使用 AllocDosObject() 的唯一真正原因是在 AmigaOS 中的長字對齊要求(但在 MorphOS 或 AROS 中不是)。在過去,它曾經是許多錯誤的來源,因為開發人員不知道長字對齊限制,導致隨機行為/記憶體損壞。

void CreateQPort(struct MsgPort *port)
{
	port->mp_Node.ln_Type = NT_MSGPORT;
	port->mp_Flags        = PA_SIGNAL;
	if ((BYTE) (port->mp_SigBit = AllocSignal(-1)) == -1)
	{
		port->mp_SigBit = SIGB_SINGLE;
		SetSignal(0, SIGF_SINGLE);
	}
	port->mp_SigTask      = FindTask(NULL);  // SysBase->ThisTask would be faster here
	NEWLIST(&port->mp_MsgList);
}

void DeleteQPort(struct MsgPort *port)
{
	if (port->mp_SigBit == SIGB_SINGLE)
	{
		SetSignal(0, SIGF_SINGLE);
	}
	else
	{
		FreeSignal(port->mp_SigBit);
	}
}

如何使用 dos.library/DateToStr() 以字串格式返回格式為 DDMMYYYY 的日期?

#include <dos/dos.h>
#include <proto/dos.h>
#include <dos/datetime.h>

int main(void)
{
    struct DateTime curr;
    char day[LEN_DATSTRING];
    char time[LEN_DATSTRING];
    char date[LEN_DATSTRING];
    struct DateStamp stamp;

    curr.dat_Format  = FORMAT_DOS;
    curr.dat_Flags   = 0;
    curr.dat_StrDay  = day;
    curr.dat_StrDate = date;
    curr.dat_StrTime = time;

    DateStamp(&curr.dat_Stamp);
    DateToStr(&curr);
    Printf("Current time: %s, %s, %s\n", day, date, time);
    
    BPTR fh = Open("__TEST__", MODE_NEWFILE);
    
    if (fh != NULL)
    {
        struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
        
        if (fib != NULL)
        {
            if (ExamineFH(fh, fib))
            { 
        	curr.dat_Stamp = fib->fib_Date;
        	DateToStr(&curr);
                Printf("File modification time: %s, %s, %s\n", day, date, time);
            }
            else
        	PrintFault(IoErr(), "Examine failed");

            Printf("Waiting 5 seconds\n");
            Delay(5*50);
            
            DateStamp(&stamp);
            
            Printf("Calling SetFileDate\n");
            if(SetFileDate("__TEST__", &stamp)) {
                if (ExamineFH(fh, fib))
                { 
                    curr.dat_Stamp = fib->fib_Date;
                    DateToStr(&curr);
                    Printf("New file modification time: %s, %s, %s\n", day, date, time);
                }
                else
                    PrintFault(IoErr(), "Examine failed");        	
            }
            else
        	PrintFault(IoErr(), "SetFileDate");
            
            FreeDosObject(DOS_FIB, fib);
        }
        else 
            PrintFault(IoErr(), "Couldn't alloc FileInfoBlock");
            
        Close(fh);
        DeleteFile("__TEST__");
    }
    else
	PrintFault(IoErr(), "Couldn't create file");
    
    return 0;
}

保護檔案和目錄

[編輯 | 編輯原始碼]

你可以保護檔案和目錄免受意外刪除或更改。你可以透過呼叫 SetProtection() 函式來實現,該函式會根據 FIB FileInfoBlock 結構中所需的保護位來更改保護位。

#include <proto/dos.h>
#include <stdio.h>

int main(void)
{
    TEXT buffer[512];
    BPTR fh = Open("__TEST__", MODE_NEWFILE);
    
    if (fh != NULL)
    {
        if (NameFromFH(fh, buffer, 512))
        {
            printf("got name: %s\n", buffer);
        }
        else
        {
            printf("namefromlock failed. ioerr = %ld\n", IoErr());
        }
        
        Close(fh);
        DeleteFile("__TEST__");
    }
    else
    {
        printf("couldn't create file\n");
    }

    return 0;
}
#include <proto/dos.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char c;
    if (argc < 2)
    {
        fprintf(stderr, "usage: %s <varname>\n", argv[0]);
        return 20;
    }
	
    if (GetVar(argv[1], &c, 1, GVF_BINARY_VAR) == 0)
    {
        LONG len = IoErr();
	char *buf = malloc(len + 1);
	if (!buf)
	{
	    PrintFault(ERROR_NO_FREE_STORE, argv[0]);
	    return 20;
	}
	
	printf("IoErr() says the len of the value of the var '%s' is: %ld\n", argv[1], len);
	
	
	len = GetVar(argv[1], buf, len+1, GVF_BINARY_VAR);
	
	printf("GetVar() says the len of the value of the var '%s' is: %ld - its value is '%s'\n",
	       argv[1], len, buf);
	
	free(buf);
	
	return 0;
    }

    PrintFault(IoErr(), argv[1]);    
    
    return 20;
}

FileInfoBlock 看起來像這樣

struct FileInfoBlock {
  LONG fib_DiskKey;
  LONG fib_DirEntryType;
  char fib_FileName[108];
  LONG fib_Protection;
  LONG fib_EntryType;
  LONG fib_Size;
  LONG fib_NumBlocks;
  struct DateStamp fib_Date;
  char fib_Comment[80];
  char fib_Reserved[36];
};
*fib_DiskKey:      Key number for the disk. Usually of no interest for us.
*fib_DirEntryType: If the number is smaller than zero it is a file. On the other hand, if the number is larger than zero it is a directory.
*fib_FileName:     Null terminated string containing the filename. (Do not use longer filenames than 30 characters.) FileInfoBlock fib_Comment and fib_FileName should be BCPL strings would make porting of ftp-handler easier and Staf wrote that BCPL strings will become BCPL strings on all platforms. 
*fib_Protection:   Field containing the protection flags:
                  FIBF_DELETE  : the file/directory can not be deleted.
                  FIBF_EXECUTE : the file can not be executed.
                  FIBF_WRITE   : you can not write to the file.
                  FIBF_READ    : you can not read the file.
                  FIBF_ARCHIVE : Archive bit.
                  FIBF_PURE    : Pure bit.
                  FIBF_SCRIPT  : Script bit.
                  (Note! All of the flags are for the moment not working!)

*fib_EntryType:   File/Directory entry type number. Usually of no interest for us.
*fib_Size:        Size of the file (in bytes).
*fib_NumBlocks:   Number of blocks in the file.
*fib_Date:        Structure containing the date when the file was latest updated/created.
*fib_Comment:     Null terminated string containing the file comment. (Max 80 characters including the NULL sign.) 
The Amiga Dos/Examine() docs say that the buffers are ASCIIZ when Dos/Examine() returns. The only confusion was what the *handler* fills in the FIB on an ACTION_EXAMINE_OBJECT. On m68k it was always BCPL-style. There was some idea that on non-m68k handlers should use ASCIIZ, to save the BCPL2ASCIIZ step in Dos/Examine(). The *handler* always fills in as BCPL style, and Dos/Examine() always runs a BCPL2ASCIIZ conversion on fib_Comment and fib_FileName *regardless* of architecture. Especially since a length-terminated string is quicker to convert to a null-terminated string than the other way around. Only handler writers need to be aware of these details. 
*fib_Reserved:    This field is for the moment reserved, and may therefore not be used.
#include <dos/dos.h>
#include <proto/dos.h>
#include <stdio.h>

int main(void)
{
    BPTR fh = Open("__TEST__", MODE_NEWFILE);
    
    if (fh != NULL)
    {
        struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
        
        if (fib != NULL)
        {
            if (ExamineFH(fh, fib))
            {
                printf("got fib.filename = %s\n", fib->fib_FileName);
            }
            else
            {
                printf("examinefh failed, ioerr = %ld\n", IoErr());
            }
            FreeDosObject(DOS_FIB, fib);
        }
        else
        {
            printf("couldn't allocate fileinfoblock\n");
        }
            
        Close(fh);
        DeleteFile("__TEST__");
    }
    else
    {
        printf("couldn't create file\n");
    }

    return 0;
}

目前可以使用一些程式碼獲取“雙擊”檔案的名稱

if (argc == 0)   
{   
    struct WBStartup *startup = (struct WBStartup *) argv;   
    if (startup->sm_NumArgs > 1)   
    {   
        BPTR  parentlock = startup->sm_ArgList[1].wa_Lock;   
        char *filename   = startup->sm_ArgList[1].wa_Name;   
    }   
}

但無法使用“GetCurrentDirName”獲取我的檔案所在的目錄。如果將 CLI 設定為 ToolType,GetCurrentDirName 會起作用,但隨後會獲取錯誤的名稱。由於你的程式是由 Workbench 啟動的,因此它實際上沒有 CLI 結構,因此 GetCurrentDirName() 無法在沒有 CLI 工具型別的情況下工作。但你應該能夠使用 NameFromLock() 並從那裡找到你需要的東西。

BPTR GetCurrentDir (void) {
    BPTR dir;
    dir = CurrentDir(0);
    CurrentDir(dir);
    return dir;
}

GetCurrentDirName() 從程式的 CLI 結構中獲取當前路徑字串,如果不存在 CLI 結構,則返回錯誤(IoErr() == ERROR_OBJECT_WRONG_TYPE)。

#include <proto/exec.h>
#include <proto/dos.h>
#include <dos/dostags.h>

/*
BOOL myGetCurrentDirName (STRPTR buffer,LONG length)

{
BPTR lock;
BOOL success = FALSE;

if (lock = Lock ("",SHARED_LOCK))
    {
    success = NameFromLock (lock,buffer,length);
    UnLock (lock);
    }

return (success);
}
*/

{
struct Process *pr = (struct Process *) FindTask (NULL);
struct Message *msg;
BPTR win;

WaitPort (&pr->pr_MsgPort);
msg = GetMsg (&pr->pr_MsgPort);

win = Open ("con:////DirName/CLOSE/WAIT",MODE_NEWFILE);
if (win)
    {
    char buffer[256];

    if (GetCurrentDirName (buffer,256))
        {
        FPrintf (win,"Dir Name = <%s>n",buffer);
        }
    else
        {
        Fault (IoErr(),NULL,buffer,256);
        FPrintf (win,"Error: %sn",buffer);
        }

    Close (win);
    }

Forbid();
ReplyMsg (msg);
}

int main (void)

{
struct Message *msg = AllocVec (sizeof(struct Message),MEMF_CLEAR|MEMF_PUBLIC);

if (msg)
    {
    struct MsgPort *port = CreateMsgPort();
    if (port)
        {
        struct Process *pr = CreateNewProcTags (NP_Entry,proc,TAG_END);
        if (pr)
            {
            msg->mn_ReplyPort = port;
            PutMsg (&pr->pr_MsgPort,msg);
            WaitPort (port);
            GetMsg (port);
            }
        DeleteMsgPort (port);
        }
    FreeVec (msg);
    }

return (0);
}

MatchFirst() 並傳遞給 MatchNext() 和 MatchEnd()

ParsePattern() 函式建立一個區分大小寫的萬用字元字串,而 ParsePatternNoCase() 函式建立一個不區分大小寫的萬用字元字串。區分大小寫的 MatchPattern() 或不區分大小寫的 MatchPatternNoCase()

透過 ReadArgs() 使用 RDArgs 結構解析命令列

CompareDates()

AmigaDos wild card are ? # % | * [] ` ~
#include <stdlib.h>

/* Write some numbers to a file */
void WriteNumbers(void) {
  BPTR fh;
  int n;
  UBYTE numstr[5];

  printf("Writing numbers to numbers.dat\n\n");
  /* Open file for writing */
  fh = Open("numbers.dat", MODE_NEWFILE);
  if (fh) {
    for (n=1; n<=20; n++) {
      /* Convert number to a string with new line
          & write string to file */
      sprintf(numstr, "%d\n", n);
      FPuts(fh, numstr);
     }
     Close(fh);
  }
}

/* Read numbers from a file */
void ReadNumbers(void) {
  BPTR fh;
  int i;
  UBYTE numstr[5];
  UBYTE *buffer;

  printf("Reading file numbers.dat.\n");
  /* Open existing file for reading */
  fh = Open("numbers.dat", MODE_OLDFILE);
  if (fh) {
     /* Read a string and check for End of File */
     while (buffer = FGets(fh, numstr, 5L)) {
         /* Convert string to number and print it */
         i = atoi(numstr);
         printf("Number %d\n", i);
     }
     Close(fh);
  }
  printf("EOF found\n");
}

int main(void)
{
  WriteNumbers();

  ReadNumbers();

  return 0;
}
#include <stdlib.h>
#include <dos/dos.h>
#include <defines/dos.h>

extern struct SYSBase *SysBase;
extern struct DOSBase *DOSBase;

int main(int argc, char *argv[])
{
int ioerr, fsize, res;
BPTR fhandle;

if (argc != 2) {
printf("Wrong number of arguments\n");
return 1;
}

fhandle = Open(argv[1], MODE_OLDFILE);
if (!fhandle) {
ioerr = IoErr();
printf("Cannot open '%s'\n", argv[1]);
PrintFault(ioerr, "IoErr() says: ");
return 1;
}

res = Seek(fhandle, 0, OFFSET_END);
if (res == -1) {
ioerr = IoErr();
printf("Cannot seek to the end of file\n");
PrintFault(ioerr, "IoErr() says: ");
Close(fhandle);
return 1;
}

fsize = Seek(fhandle, 0, OFFSET_BEGINNING);
printf("File size = %d\n", fsize);

Close(fhandle);
return 0;
}

檔案系統

[編輯 | 編輯原始碼]

裝置、處理程式和檔案系統必須是可重入的。

處理程式和檔案系統的區別在於,檔案系統需要公開目錄結構和檔案。隱含地,這意味著大約 60% 的 dos 包。

一般的經驗法則是,將“虛擬”裝置(沒有底層硬體)實現為處理程式,而不是像 ram-handler 那樣實現為完整的檔案系統。顯然,這不是鐵律,但它是 OS 中的既定模式。

模擬外來檔案系統屬於完整檔案系統的類別,但是,由於虛擬裝置沒有底層硬體,因此對裝置的需求可能是一種浪費,最好將其實現為 fat-handler。

在“Assign DISMOUNT”之後?我不知道,但是 AROS 的 Assign 命令沒有呼叫 ACTION_END,而 ACTION_END 是完全關閉處理程式所必需的?

檔案系統可以使用 LDF_READ|LDF_DEVICES 鎖定,但可以使用 LDF_READ|LDF_VOLUMES 解鎖。

不應從 dl_DosList 中刪除裝置條目,也不應該解除安裝程式碼,直到所有關聯的鎖(直接或間接)都被釋放。具體來說,如果一個裝置掛載了介質(又名卷),任何嘗試刪除裝置的嘗試,無論是硬體裝置還是虛擬裝置,都必須被拒絕。檢測裝置是否掛載的最佳方法是在不觸發提示使用者“請插入卷 BLAH:”的請求的情況下,使用 LockDosList \ AttemptLockDosList,然後使用 FindDOSEntry。

struct Process * this_process; 
APTR old_window_ptr; 
BPTR lock;

this_process = (struct Process *)FindTask(NULL);

old_window_ptr = this_process->pr_WindowPtr; 
this_process->pr_WindowPtr = (APTR)-1;

UnLock(lock = Lock("device_name:",SHARED_LOCK));

this_process->pr_WindowPtr = old_window_ptr;

如果獲得的鎖不是 (BPTR)NULL,則你嘗試的裝置/卷/分配可用,否則不可用。請注意,此方法不專門檢查裝置,你必須掃描 DOSList 以進行檢查。

鑑於 dl_Volume 和 dl_Device 是“配對”的,任何嘗試刪除裝置的嘗試都應驗證裝置的卷及其關聯的鎖,並返回一個失敗,並帶有一個適當的錯誤程式碼。

fm2000 原始碼文件

檔案系統決定最大大小,只要你正確地與檔案系統通訊,>4GB 是可能的。

它們僅受以下因素限制 -

  1. 在 64 位 DOS 擴展出現之前編寫的軟體,
  2. 沒有使用這些擴充套件的軟體,因為沒有實際的“API”呼叫替換,它們需要直接與檔案系統通訊。

第一步是新增 64 位支援,以便它可以處理大於 4GB 的分割槽。在快取中添加了新的程式碼來探測底層裝置,以檢視它是否支援 64 位擴充套件,然後如果收到對超過 4GB 邊界的資料的請求,則使用 64 位讀或寫操作而不是標準操作(或者出錯,如果探測沒有找到任何 64 位擴充套件)。在 Amiga 世界中,有三種常用的 64 位擴充套件 - TD64(即 TrackDisk64)、新型 TD64 和 DirectSCSI。前兩個是支援的,但 DirectSCSI 應該不難新增。

實現寫回快取,其中處理程式要求快取寫入一些資料,快取立即報告成功,但僅將資料標記為“待寫入”。然後,以固定間隔(例如五秒鐘)將所有這些“髒”塊一次性寫入磁碟。這使得使用者感覺速度更快,並且有可能減少磁碟活動(== 減少磨損和降低功耗),但存在在發生電源故障或裝置丟失(例如拔出磁碟)的情況下丟失資料的風險。通常,可移動介質使用直寫快取(即立即寫入),而固定磁碟使用寫回快取。

由於這需要一個單獨的任務來坐等並在被呼叫時重新整理髒塊,這意味著快取需要鎖定。如果檔案系統想要多執行緒(而快取實際上是在 cache.library 中,對所有程式都可用),將來也需要鎖定。到目前為止,在快取操作周圍有鎖定,但在塊操作周圍沒有鎖定。

將讀鎖升級為寫鎖。通常在獲取寫鎖之前需要先釋放原始鎖,這意味著有一段時間內不會持有任何鎖,其他程序可能會在這個時間視窗內搶佔鎖。POSIX 執行緒的解決方法需要使用條件變數,而 AROS 訊號量目前還沒有條件變數。

參考文獻

[編輯 | 編輯原始碼]

資料包

[編輯 | 編輯原始碼]

資料包(封裝在 Dos 呼叫中的小訊息)被髮送到檔案系統或處理程式,可以按如下分類:

* Basic Input/Output (handlers and file systems) - Open, Read (ACTION_READ), Write (ACTION_WRITE), Seek, Close, LockRecord, UnLockRecord, SetFileSize,
* File/Directory Manipulation/Information (handlers) - Lock, DupLock, UnLock, Examine, ExNext, ExAll, CreateDir, Rename, DeleteFile, 
* Volume Manipulation/Information - Info (ACTION_DISK_INFO), Relabel (ACTION_RENAME_DISK), Format (ACTION_FORMAT), 
* Handler Maintenance and Control - AddBuffers (ACTION_MORE_CACHE), Inhibit (ACTION_INHIBIT), 
* Handler Internal - ACTION_NIL, ACTION_READ_RETURN, ACTION_WRITE_RETURN, ACTION_TIMER 
* Console Only Packets (handler only - ignored by file system) - WaitForChar (ACTION_WAIT_CHAR), 
struct DosPacket {
   struct Message *dp_Link;	 /* EXEC message	      */
   struct MsgPort *dp_Port;	 /* Reply port for the packet */
				 /* Must be filled in each send. */
   LONG dp_Type;                 /* See ACTION_... 'R' means Read, 'W' means Write to the file system */
   LONG dp_Res1;                 /* result that would have been returned by the function, 
                                    e.g. Write ('W') returns actual
				  * length written */
   LONG dp_Res2;                 /* For file system calls - returned by IoErr() */
/*  Device packets common equivalents */
#define dp_Action  dp_Type
#define dp_Status  dp_Res1
#define dp_Status2 dp_Res2
#define dp_BufAddr dp_Arg1
   LONG dp_Arg1;
   LONG dp_Arg2;
   LONG dp_Arg3;
   LONG dp_Arg4;
   LONG dp_Arg5;
   LONG dp_Arg6;
   LONG dp_Arg7;
}; 
struct StandardPacket {
   struct Message   sp_Msg;
   struct DosPacket sp_Pkt;
}; /* StandardPacket */

/* Packet types */
#define ACTION_NIL		0
#define ACTION_STARTUP		0
#define ACTION_GET_BLOCK	2	/* OBSOLETE */
#define ACTION_SET_MAP		4
#define ACTION_DIE		5
#define ACTION_EVENT		6
#define ACTION_CURRENT_VOLUME	7
#define ACTION_LOCATE_OBJECT	8
#define ACTION_RENAME_DISK	9
#define ACTION_WRITE		'W'
#define ACTION_READ		'R'
#define ACTION_FREE_LOCK	15
#define ACTION_DELETE_OBJECT	16
#define ACTION_RENAME_OBJECT	17
#define ACTION_MORE_CACHE	18
#define ACTION_COPY_DIR		19
#define ACTION_WAIT_CHAR	20
#define ACTION_SET_PROTECT	21
#define ACTION_CREATE_DIR	22
#define ACTION_EXAMINE_OBJECT	23
#define ACTION_EXAMINE_NEXT	24
#define ACTION_DISK_INFO	25
#define ACTION_INFO		26
#define ACTION_FLUSH		27
#define ACTION_SET_COMMENT	28
#define ACTION_PARENT		29
#define ACTION_TIMER		30
#define ACTION_INHIBIT		31
#define ACTION_DISK_TYPE	32
#define ACTION_DISK_CHANGE	33
#define ACTION_SET_DATE		34
#define ACTION_SCREEN_MODE	994
#define ACTION_READ_RETURN	1001
#define ACTION_WRITE_RETURN	1002
#define ACTION_SEEK		1008
#define ACTION_FINDUPDATE	1004
#define ACTION_FINDINPUT	1005
#define ACTION_FINDOUTPUT	1006
#define ACTION_END		1007
#define ACTION_SET_FILE_SIZE	1022	/* fast file system only in 1.3 */
#define ACTION_WRITE_PROTECT	1023	/* fast file system only in 1.3 */

/* new 2.0 packets */
#define ACTION_SAME_LOCK	40
#define ACTION_CHANGE_SIGNAL	995
#define ACTION_FORMAT		1020
#define ACTION_MAKE_LINK	1021
/**/
/**/
#define ACTION_READ_LINK	1024
#define ACTION_FH_FROM_LOCK	1026
#define ACTION_IS_FILESYSTEM	1027
#define ACTION_CHANGE_MODE	1028
/**/
#define ACTION_COPY_DIR_FH	1030
#define ACTION_PARENT_FH	1031
#define ACTION_EXAMINE_ALL	1033
#define ACTION_EXAMINE_FH	1034
#define ACTION_LOCK_RECORD	2008
#define ACTION_FREE_RECORD	2009
#define ACTION_ADD_NOTIFY	4097
#define ACTION_REMOVE_NOTIFY	4098
#define	ACTION_SERIALIZE_DISK	4200
struct ErrorString {
	LONG  *estr_Nums;
	UBYTE *estr_Strings;
};

例如,AFS 處理程式尚未完全“dos 資料包化”。(“真實”dos 資料包和 FSA 內容不能一起工作)它們可以。我們與 Rob Norris 討論過這個問題。基本思路是檢查檔案控制代碼或鎖的 Port(或 Process)成員指向的內容。它既可以是 NT_PROCESS,也可以是 NT_DEVICE 型別的節點。這樣就可以區分你正在與誰通訊。此外,你的 con.handler 應該能夠與 packet.handler 很好地協作。如果不能,則需要修復 packet.handler。

有一些問題,都與 AROS 的硬編碼 32 位值等有關(例如,fib_Size)。對此最好的解決方法是什麼?MorphOS 使用 DOS 函式和結構的 64 位版本,但我不知道 OS4 是不是這樣。

OS4 使用以下 64 位資料包型別,因為我被要求在幾年前為 WinUAE 目錄檔案系統模擬提供支援。

#define ACTION_CHANGE_FILE_POSITION64  8001
#define ACTION_GET_FILE_POSITION64    8002
#define ACTION_CHANGE_FILE_SIZE64      8003
#define ACTION_GET_FILE_SIZE64        8004

舊的資料包在實際大小大於等於 2G 時會返回檔案大小為 2GB-1。我想我們需要更改 DoPkt 等函式以返回 QUAD?據我所知,DoPkt 和內部 dopacket 都需要更改以返回 QUAD,並且 DosPacket->dp_Res1 需要是 QUAD(據我所知,這會破壞相容性)。我認為這比嘗試將所有 dos 結構更改為使用 64 位值更可取,是嗎?

如果說訪問 blocknr/size/etc 的程式碼應該檢查它們是否等於 0x7FFFFFFF,如果等於,則直接使用 64 位資料包呼叫,這樣夠了嗎?

MorphOS、phase5、Ralph Babel 和一些 CBM 工程師定義了 TD64 標準。MorphOS dosextens.h 描述了他們的 64 位資料包擴充套件。

TD64 與 64 位 DOS 檔案訪問無關。是的,我們長期以來一直支援 TD64(以及 NSD)。如果沒有它,AROS 就無法訪問任何硬碟的第一個 4GB 以上的部分。

普通任務的限制在於它們不能呼叫 dos.library 的函式,也不能呼叫可能呼叫 dos.library 函式的函式。程序沒有這個限制。

程序是一種擴充套件的任務。與任務相反,它可以使用 dos.library 的函式,因為程序結構包含一些特殊欄位,涉及檔案和目錄。

struct Process {
    struct    pr_Task;    
    struct    pr_MsgPort; 
    WORD      pr_pad;
    BPTR      pr_SegList;
    LONG      pr_stackSize;
    APTR      pr_GlobVec;
    LONG      pr_TaskNum;
    BPTR      pr_StackBase;
    LONG      pr_Result2;
    BPTR      pr_CurrentDir;
    BPTR      pr_CIS;
    BPTR      pr_COS;
    APTR      pr_ConsoleTask;
    APTR      pr_FileSystemTask;
    BPTR      pr_CLI;
    APTR      pr_ReturnAddr;
    APTR      pr_PktWait;
    APTR      pr_WindowPtr;
    BPTR      pr_HomeDir;
    LONG      pr_Flags;
    APTR      pr_ExitCode;
    LONG      pr_ExitData;
    APTR      pr_Arguments;
    struct    pr_LocalVrs;
    APTR      pr_ShellPrivate;
    BPTR      pr_CES;
};

pr_Flags 標誌

prb_FreeSegList
prb_FreeCurrDir
prb_FreeCli
prb_CloseInput
prb_CloseOutput
prb_FreeArgs

這比把所有東西都塞進全域性變數要乾淨。假設你希望在稍微不同的資料上執行相同的執行緒程式碼(除非你有多個 CPU 核心,否則這樣做意義不大,但無論如何),NP_UserData 允許你一遍又一遍地使用相同的程式碼,只需傳遞不同的起始資料即可。當然,你可以設定一個訊息埠,並以這種方式進行操作,但是對於簡單任務來說,傳遞一個指向資料的直接指標並使用訊號量更容易。你幾乎可以共享程序之間的任何庫指標。

每個使用 bsdsocket.library 的程序都必須自行開啟該庫。不允許共享 bsdsocketbase。

一般來說,任務應該共享相同的地址空間,而程序不應共享,至少出於這個原因,每個程序都應該單獨重新開啟庫。

由於人們不遵守這些規則,因此無法在系統中引入記憶體保護機制。

最簡單的方法是呼叫 CreateNewProc(),不帶 NP_Input、NP_Output、NP_Error 的標記,這樣會為子程序開啟一個預設的“NIL:”流。

int ProcEntry () { 
struct Process *proc; 
struct Message *msg; 
proc = (struct Process *)FindTask(NULL); 
WaitPort(&proc->pr_MsgPort); 
msg = GetMsg(&proc->pr_MsgPort); 
/* ... */ 
}

正確地(使用訊號量)保護任何你打算線上程之間共享的資源,例如 IO 控制代碼等,所有這些都應該沒問題。

CreateNewProc() 不應該建立 BCPL 堆疊幀和暫存器設定(A5=BCPL_jsr 和其他相關的奇怪行為),只有 RunCommand() 需要這樣做。可以理解為什麼會有混淆,因為 CreateNewProc() 需要呼叫 BCPL_init(),而 BCPL_init() 實際上只執行 BCPL 設定的一部分(愚蠢的 segarray 和一些 globvec 內容),但它不應該設定 BCPL 堆疊幀(當所有程序條目都期望 SysBase = 崩潰時,它會用 BCPL_rts 替換 A6)。

CreateNewProc()->CallEntry() 應該與以前一樣。

 argPtr = A0, argSize = D0, SysBase = A6, no stack swap

RunProcess() 應該包含瘋狂的 BCPL 內容。

為什麼要進行這種更改?我不明白它對其他架構有什麼幫助,它肯定對 m68k 沒有幫助。但它仍然應該設定 pr_ReturnAddr,對吧?不,因為 pr_ReturnAddr 僅由 RunProcess()/RunCommand() 的退出使用。

dos/Exit() 適用於使用 Create(New)Proc() 啟動的正常程序。它不是 BCPL 特定的。(但也許它在“BCPL 模式”下的工作方式不同,我還沒有測試過)AROS/DOS/Exit() 應該是一個空操作。Exit() 的唯一呼叫者是 BCPL 例程,並且在 arch/m68k-amiga/dos/bcpl.S 中有一個 BCPL 特定的實現來支援這些舊程式。

Dos/Exit() 是 AmigaOS 中的一個錯誤,我們不應該把它變成一個一等函式。要實現一個 C 風格的退出,它應該在 AROS C 庫中,實現記憶體釋放和庫關閉等功能。

瞭解更多資訊 請點選這裡

DateStamp 結構如下所示

struct DateStamp
{
  LONG ds_Days;      /* days since 01-Jan-1978 */
  LONG ds_Minute;    /* minutes past midnight */
  LONG ds_Tick;      /* ticks past the last minute */
};

涉及四個公共結構:DosLibrary (dl_)、RootNode (rn_)、DosInfo (di_) 和 DevInfo (dvi_)。重新審視了 DOSBase,並從中刪除了幾乎所有 AROS 特定的欄位。

dl_DevInfo - 在 struct DosInfo 中用 di_DevInfo 替換。

dl_SegList - 根本沒有使用,dos.library 是 ROM 常駐的,沒有自己的段列表。

dl_ResList - 已刪除,現在它是 DosInfo->di_ResList。

唯一剩下的就是 dl_Flags。DirectoryOpus 在 AROS 上使用了它。現在可以用 rn_Flags 正確替換它嗎,或者應該在 ABI v1 中進行此操作?其他私有欄位仍然存在,它們確實是私有的。

struct RootNode {
    BPTR rn_TaskArray;
    BPTR rn_ConsoleSegment;
    struct DateStamp rn_time;
    LONG rn_RestartSeg;
    BPTR rn_Info;
    BPTR rn_FileHandlerSegment;
};
struct DosInfo {
    BPTR di_McName;
    BPTR di_DevInfo;
    BPTR di_Devices;
    BPTR di_Handlers;
    BPTR di_NextEntry;
    LONG di_UseCount;
    BPTR di_SegPtr;
    BPTR di_SegName;
};
struct DevInfo {
    BPTR dvi_Next;
    LONG dvi_Type;
    APTR dvi_Task;
    BPTR dvi_Lock;
    BPTR dvi_Handler;
    LONG dvi_StackSize;
    LONG dvi_Priority;
    LONG dvi_Startup;
    BPTR dvi_SegList;
    BPTR dvi_GlobVec;
    BPTR dvi_Name;
};

在 FGetC-s 之後是 Seek-s,然後是 Read。所有這些都是針對同一個 file_handle。我沒有檢查引數和具體發生了什麼,但這與精確記憶體分配的模式類似:使用 'getc',你以一步一步的方式讀取檔案緩衝區,並且只計算字元或位元組,直到你到達你想要讀取的單詞、行、檔案或任何內容的結尾。然後你 'seek' 回到開始的位置,你 'alloc' 計算出的記憶體大小,然後你 'read' 整個內容一次。這是一種避免記憶體相關問題的方法,但它在 i/o 方面可能相當繁重。

與 C 中的 (f)getc 類似,FGetC 是一個旨在被頻繁呼叫的函式。它通常用作傳遞給解析函式的函式指標。使用 fscanf 等的程式會大量使用 FGetC。由於它是緩衝的,所以我懷疑它是一個性能瓶頸(除非你當然修補它,並在每次呼叫時在控制檯上列印一行)。

#include <proto/dos.h>
#include <proto/utility.h>

UBYTE get_char()
{
    UBYTE buffer;
    BPTR in = Input();

    SetMode (in,1); /* set to RAW mode */
    Read (in,&buffer,1);
    SetMode (in,0); /* set to CON mode */

    if (buffer == '\r')
    buffer = '\n';

    return (ToUpper (buffer));
}

int main (void)

{
    UBYTE key;

    Printf ("Please press a key: ");
    Flush (Output());

    key = get_char();

    Printf ("\nThe key you pressed is %lc\n",key);

    return (0);
}
BPTR Open(CONST_STRPTR name, LONG accessMode) (D1,D2)
BOOL Close(BPTR file) (D1)
LONG Read(BPTR file, APTR buffer, LONG length) (D1, D2, D3)
LONG Write(BPTR file, CONST_APTR buffer, LONG length) (D1, D2, D3)
BPTR Input() ()
BPTR Output() ()
LONG Seek(BPTR file, LONG position, LONG mode) (D1,D2,D3)
BOOL DeleteFile(CONST_STRPTR name) (D1)
LONG Rename(CONST_STRPTR oldName, CONST_STRPTR newName) (D1,D2)
BPTR Lock(CONST_STRPTR name, LONG accessMode) (D1,D2)
BOOL UnLock(BPTR lock) (D1)
BPTR DupLock(BPTR lock) (D1)
LONG Examine(BPTR lock, struct FileInfoBlock* fib) (D1,D2)
LONG ExNext(BPTR lock, struct FileInfoBlock* fileInfoBlock) (D1,D2)
LONG Info(BPTR lock, struct InfoData* parameterBlock) (D1,D2)
BPTR CreateDir(CONST_STRPTR name) (D1)
BPTR CurrentDir(BPTR lock) (D1)
SIPTR IoErr() ()
struct MsgPort* CreateProc(CONST_STRPTR name, LONG pri, BPTR segList, LONG stackSize) (D1,D2,D3,D4)
void Exit(LONG returnCode) (D1)
BPTR LoadSeg(CONST_STRPTR name) (D1)
BOOL UnLoadSeg(BPTR seglist) (D1)
struct MsgPort* RunHandler(struct DeviceNode *devnode, const char *path) (A0,A1)

struct Device *DeviceProc(CONST_STRPTR name) (D1)
LONG SetComment(CONST_STRPTR name, CONST_STRPTR comment) (D1,D2)
LONG SetProtection(CONST_STRPTR name, ULONG protect) (D1,D2)
struct DateStamp *DateStamp(struct DateStamp *date) (D1)
void Delay(ULONG timeout) (D1)
LONG WaitForChar(BPTR file, LONG timeout) (D1,D2)
BPTR ParentDir(BPTR lock) (D1)
LONG IsInteractive(BPTR file) (D1)
LONG Execute(CONST_STRPTR string, BPTR input, BPTR output) (D1,D2,D3)
APTR AllocDosObject(ULONG type, struct TagItem *tags) (D1,D2)
void FreeDosObject(ULONG type, APTR ptr) (D1,D2)

LONG DoPkt(struct MsgPort* port, LONG action, SIPTR arg1, SIPTR arg2, SIPTR arg3, SIPTR arg4, SIPTR arg5) 
void SendPkt(struct DosPacket *dp, struct MsgPort *port, struct MsgPort *replyport) (D1,D2,D3)
struct DosPacket *WaitPkt() ()
void ReplyPkt(struct DosPacket *dp, LONG res1, LONG res2) (D1,D2,D3)
void AbortPkt(struct MsgPort *port, struct DosPacket *pkt) (D1,D2)
BOOL LockRecord(BPTR fh, ULONG offset, ULONG length, ULONG mode, ULONG timeout) (D1,D2,D3,D4,D5)
BOOL LockRecords(struct RecordLock *recArray, ULONG timeout) (D1,D2)
BOOL UnLockRecord(BPTR fh, ULONG offset, ULONG length) (D1,D2,D3)
BOOL UnLockRecords(struct RecordLock * recArray) (D1)
BPTR SelectInput(BPTR fh) (D1)
BPTR SelectOutput(BPTR fh) (D1)

LONG FGetC(BPTR file) (D1)
LONG FPutC(BPTR file, LONG character) (D1,D2)
LONG UnGetC(BPTR file, LONG character) (D1,D2)
LONG FRead(BPTR fh, APTR block, ULONG blocklen, ULONG number) (D1,D2,D3,D4)
LONG FWrite(BPTR fh, CONST_APTR block, ULONG blocklen, ULONG numblocks) (D1,D2,D3,D4)
STRPTR FGets(BPTR fh, STRPTR buf, ULONG buflen) (D1,D2,D3)
LONG FPuts(BPTR file, CONST_STRPTR string) (D1,D2)
LONG VFWritef(BPTR fh, CONST_STRPTR fmt, const IPTR *argarray) (D1,D2,D3)
LONG VFPrintf(BPTR file, CONST_STRPTR format, const IPTR *argarray) (D1,D2,D3)

LONG Flush(BPTR file) (D1)
LONG SetVBuf(BPTR file, STRPTR buff, LONG type, LONG size) (D1,D2,D3,D4)
BPTR DupLockFromFH(BPTR lock) (D1)
BPTR OpenFromLock(BPTR lock) (D1)
BPTR ParentOfFH(BPTR fh) (D1)
BOOL ExamineFH(BPTR fh, struct FileInfoBlock* fib) (D1,D2)
BOOL SetFileDate(CONST_STRPTR name, const struct DateStamp *date) (D1,D2)
BOOL NameFromLock(BPTR lock, STRPTR buffer, LONG length) (D1,D2,D3)
BOOL NameFromFH(BPTR fh, STRPTR buffer, LONG length) (D1,D2,D3)
LONG SplitName(CONST_STRPTR name, ULONG separator, STRPTR buf, LONG oldpos, LONG size) (D1,D2,D3,D4,D5)
LONG SameLock(BPTR lock1, BPTR lock2) (D1,D2)
LONG SetMode(BPTR fh, LONG mode) (D1,D2)
BOOL ExAll(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control) (D1,D2,D3,D4,D5)
LONG ReadLink(struct MsgPort *port, BPTR lock, CONST_STRPTR path, STRPTR buffer, ULONG size) (D1,D2,D3,D4,D5)
LONG MakeLink(CONST_STRPTR name, APTR dest, LONG soft) (D1,D2,D3)
BOOL ChangeMode(ULONG type, BPTR object, ULONG newmode) (D1,D2,D3)
LONG SetFileSize(BPTR file, LONG offset, LONG mode) (D1,D2,D3)
SIPTR SetIoErr(SIPTR result) (D1)
BOOL Fault(LONG code, CONST_STRPTR header, STRPTR buffer, LONG len) (D1,D2,D3,D4)
BOOL PrintFault(LONG code, CONST_STRPTR header) (D1,D2)
BOOL ErrorReport(LONG code, LONG type, IPTR arg1, struct MsgPort *device) (D1,D2,D3,D4)
LONG DisplayError(CONST_STRPTR formatStr, ULONG flags, APTR args) (A0,D0,A1)
struct CommandLineInterface *Cli() ()
struct Process *CreateNewProc(const struct TagItem *tags) (D1)
   NP_Seglist, NP_FreeSeglist, NP_Entry, NP_Input, NP_Output, NP_Error, NP_CloseInput, NP_CloseOutput, NP_CloseError
   NP_CurrentDir, NP_StackSize, NP_Name, NP_Priority, NP_CopyVars, NP_Path, NP_Arguments, NP_ConsoleTask
   NP_WindowPtr, NP_HomeDir, NP_Cli, NP_CommandName, NP_NotifyOnDeath, NP_Synchronous, NP_ExitCode, NP_ExitData
   NP_UserData
   TAG_DONE
LONG RunCommand(BPTR segList, ULONG stacksize, STRPTR argptr, ULONG argsize) (D1,D2,D3,D4)

struct MsgPort *GetConsoleTask() ()
struct MsgPort *SetConsoleTask(struct MsgPort *handler) (D1)
struct MsgPort *GetFileSysTask() ()
struct MsgPort *SetFileSysTask(struct MsgPort *task) (D1)
STRPTR GetArgStr() ()
STRPTR SetArgStr(CONST_STRPTR string) (D1)
struct Process *FindCliProc(ULONG num) (D1)
ULONG MaxCli() ()
BOOL SetCurrentDirName(CONST_STRPTR name) (D1)
BOOL GetCurrentDirName(STRPTR buf, LONG len) (D1,D2)
BOOL SetProgramName(CONST_STRPTR name) (D1)
BOOL GetProgramName(STRPTR buf, LONG len) (D1,D2)
BOOL SetPrompt(CONST_STRPTR name) (D1)
BOOL GetPrompt(STRPTR buf, LONG len) (D1,D2)
BPTR SetProgramDir(BPTR lock) (D1)
BPTR GetProgramDir() ()
LONG SystemTagList(CONST_STRPTR command, const struct TagItem *tags) (D1,D2)
LONG AssignLock(CONST_STRPTR name, BPTR lock) (D1,D2)
BOOL AssignLate(CONST_STRPTR name, CONST_STRPTR path) (D1,D2)
BOOL AssignPath(CONST_STRPTR name, CONST_STRPTR path) (D1,D2)
BOOL AssignAdd(CONST_STRPTR name, BPTR lock) (D1,D2)
LONG RemAssignList(CONST_STRPTR name, BPTR lock) (D1,D2)
struct DevProc *GetDeviceProc(CONST_STRPTR name, struct DevProc *dp) (D1,D2)
void FreeDeviceProc(struct DevProc *dp) (D1)
struct DosList *LockDosList(ULONG flags) (D1)
void UnLockDosList(ULONG flags) (D1)
struct DosList *AttemptLockDosList(ULONG flags) (D1)

LONG RemDosEntry(struct DosList *dlist) (D1)
LONG AddDosEntry(struct DosList *dlist) (D1)
struct DosList *FindDosEntry(struct DosList *dlist, CONST_STRPTR name, ULONG flags) (D1,D2,D3)
struct DosList *NextDosEntry(struct DosList *dlist, ULONG flags) (D1,D2)
struct DosList * MakeDosEntry(CONST_STRPTR name, LONG type) (D1,D2)
void FreeDosEntry(struct DosList *dlist) (D1)
BOOL IsFileSystem(CONST_STRPTR devicename) (D1)
BOOL Format(CONST_STRPTR devicename, CONST_STRPTR volumename, ULONG dostype) (D1,D2,D3)
LONG Relabel(CONST_STRPTR drive, CONST_STRPTR newname) (D1,D2)
LONG Inhibit(CONST_STRPTR name, LONG onoff) (D1,D2)
BOOL AddBuffers(CONST_STRPTR devicename, LONG numbuffers) (D1,D2)
LONG CompareDates(const struct DateStamp *date1, const struct DateStamp *date2) (D1,D2)
BOOL DateToStr(struct DateTime *datetime) (D1)
BOOL StrToDate(struct DateTime *datetime) (D1)
BPTR InternalLoadSeg(BPTR fh, BPTR table, LONG_FUNC *functionarray, LONG *stack) (D0,A0,A1,A2)
BOOL InternalUnLoadSeg(BPTR seglist, VOID_FUNC freefunc) (D1,A1)
BPTR NewLoadSeg(CONST_STRPTR file, const struct TagItem *tags) (D1,D2)
BOOL AddSegment(CONST_STRPTR name, BPTR seg, LONG type) (D1,D2,D3)
struct Segment *FindSegment(CONST_STRPTR name, struct Segment *seg, BOOL system) (D1,D2,D3)
LONG RemSegment(struct Segment *seg) (D1)
LONG CheckSignal(LONG mask) (D1)

struct RDArgs *ReadArgs(CONST_STRPTR template, IPTR *array, struct RDArgs *rdargs) (D1,D2,D3)
LONG FindArg(CONST_STRPTR template, CONST_STRPTR keyword) (D1,D2)
LONG ReadItem(STRPTR buffer, LONG maxchars, struct CSource *input) (D1,D2,D3)
LONG StrToLong(CONST_STRPTR string, LONG *value) (D1,D2)
LONG MatchFirst(CONST_STRPTR pat, struct AnchorPath *AP) (D1,D2)
LONG MatchNext(struct AnchorPath *AP) (D1)
void MatchEnd(struct AnchorPath *AP) (D1)
LONG ParsePattern(CONST_STRPTR Source, STRPTR Dest, LONG DestLength) (D1,D2,D3)
BOOL MatchPattern(CONST_STRPTR pat, CONST_STRPTR str) (D1,D2)
BPTR Error() ()
void FreeArgs(struct RDArgs *args) (D1)

BPTR SelectError(BPTR fh) (D1)
STRPTR FilePart(CONST_STRPTR path) (D1)
STRPTR PathPart(CONST_STRPTR path) (D1)
BOOL AddPart(STRPTR dirname, CONST_STRPTR filename, ULONG size) (D1,D2,D3)
BOOL StartNotify(struct NotifyRequest *notify) (D1)
void EndNotify(struct NotifyRequest *notify) (D1)
BOOL SetVar(CONST_STRPTR name, CONST_STRPTR buffer, LONG size, LONG flags) (D1,D2,D3,D4)
LONG GetVar(CONST_STRPTR name, STRPTR buffer, LONG size, LONG flags) (D1,D2,D3,D4)
LONG DeleteVar(CONST_STRPTR name, ULONG flags) (D1,D2)
struct LocalVar *FindVar(CONST_STRPTR name, ULONG type) (D1,D2)
STRPTR DosGetLocalizedString(LONG stringNum) (D1)
IPTR CliInitNewcli(struct DosPacket *dp) (A0)
IPTR CliInitRun(struct DosPacket *dp) (A0)
LONG WriteChars(CONST_STRPTR buf, ULONG buflen) (D1,D2)
LONG PutStr(CONST_STRPTR string) (D1)
LONG VPrintf(CONST_STRPTR format, IPTR *argarray) (D1,D2)
LONG Pipe(CONST_STRPTR name, BPTR *reader, BPTR *writer) (D1,D2,D3)
LONG ParsePatternNoCase(CONST_STRPTR Source, STRPTR Dest, LONG DestLength) (D1,D2,D3)
BOOL MatchPatternNoCase(CONST_STRPTR pat, CONST_STRPTR str) (D1,D2)
STRPTR DosGetString(LONG stringNum) (D0)
BOOL SameDevice(BPTR lock1, BPTR lock2) (D1,D2)
void ExAllEnd(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control)  
BOOL SetOwner(CONST_STRPTR name, ULONG owner_info) (D1,D2)
LONG ScanVars(struct Hook * hook, ULONG flags, APTR userdata) (D1,D2,D3)
華夏公益教科書