الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

[ تمت الإجابة ]ما هو بديل fork و pipe في الوندوز؟

بدأه asm-soft في 1 نوفمبر 2012 · 3 رد · 1,117 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله وبركاتة

في اللينوكس يمكن إنشاء process و تمرير البيانات بين child process و parent process بواسطة pipe و هذا شرح لها مستخدماً fork

https://www.gnu.org/...ing-a-Pipe.html

و الأن أقوم بإنشاء برنامج و أريد إنشاء منه نسخة تعمل على وندوز و لكن للأسف لم أحصل على ما يعادل ما سبق ذكره و الحل الذي أمامي الأن هو الإعتماد على Thread فقط و لكن هذا يؤثر على كفائة البرنامج فعند وجود خطأ فسوف يغلق البرنامج تماماً

عرفت أن الوندوز توفر دالة تسمى CreateProcess و لكن للأسف لم أحصل على أي مثال للتعامل معها فهل يوجد لدى أحدكم مثال؟

و ما هي الدوال البديلة لـ pipe على الوندوز التي يمكن أن تستخدم مع CreateProcess ؟

بمعنى أنني أريد هذا المثال يعمل على وندوز بدون حلول وسيطة مثل Cygwin sad.gif

 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>

 /* Read characters from the pipe and echo them to stdout. */

 void
 read_from_pipe (int file)
 {
   FILE *stream;
   int c;
   stream = fdopen (file, "r");
   while ((c = fgetc (stream)) != EOF)
 	putchar (c);
   fclose (stream);
 }

 /* Write some random text to the pipe. */

 void
 write_to_pipe (int file)
 {
   FILE *stream;
   stream = fdopen (file, "w");
   fprintf (stream, "hello, world!\n");
   fprintf (stream, "goodbye, world!\n");
   fclose (stream);
 }

 int
 main (void)
 {
   pid_t pid;
   int mypipe[2];

   /* Create the pipe. */
   if (pipe (mypipe))
 	{
   	fprintf (stderr, "Pipe failed.\n");
   	return EXIT_FAILURE;
 	}

   /* Create the child process. */
   pid = fork ();
   if (pid == (pid_t) 0)
 	{
   	/* This is the child process.
      	Close other end first. */
   	close (mypipe[1]);
   	read_from_pipe (mypipe[0]);
   	return EXIT_SUCCESS;
 	}
   else if (pid < (pid_t) 0)
 	{
   	/* The fork failed. */
   	fprintf (stderr, "Fork failed.\n");
   	return EXIT_FAILURE;
 	}
   else
 	{
   	/* This is the parent process.
      	Close other end first. */
   	close (mypipe[0]);
   	write_to_pipe (mypipe[1]);
   	return EXIT_SUCCESS;
 	}
 }

إضافة:

هناك دالة أخرى اسمها ZwCreateProcess فربما تكون هي المفيدة و لكني مازلت لا أعلم كيفيه التعامل معها.

شكراً

تم تعديل هذه المشاركة بواسطة asm-soft في 1 نوفمبر 2012 في 14:51

forum-signature-01.gif
#2

لايوجد بديل لـfork على ويندوز, لكن يُمكن محاكاتها بإستخدام ZwCreateProcess, دالة غير موثّقة, لكن الأمر ليس بتلك السهولة. هذه مثال لمحاكاتها إقتطعته من هنا:

الملف fork.h :

#ifndef __forkWindows_H__
#define __forkWindows_H__

#ifdef __cplusplus
extern "C" {
#endif

int fork(void);

#ifdef __cplusplus
};
#endif

#endif /* __forkWindows_H__ */

الملف التنفيذي forkWindows.c :

/*
* Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
* Copyright (C) DIGITEO - 2010 - Allan CORNET
* 
* This file must be used under the terms of the CeCILL.
* This source file is licensed as described in the file COPYING, which
* you should have received as part of this distribution.  The terms
* are also available at    
* http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
*
*/
/*--------------------------------------------------------------------------*/ 
#include <windows.h>
#include <WinNT.h>
#include <setjmp.h>

/*--------------------------------------------------------------------------*/ 
typedef LONG NTSTATUS;
/*--------------------------------------------------------------------------*/ 
typedef struct _SYSTEM_HANDLE_INFORMATION 
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
/*--------------------------------------------------------------------------*/ 
typedef struct _OBJECT_ATTRIBUTES 
{
    ULONG Length;
    HANDLE RootDirectory;
    PVOID /* really PUNICODE_STRING */  ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;       /* type SECURITY_DESCRIPTOR */
    PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
/*--------------------------------------------------------------------------*/ 
typedef enum _MEMORY_INFORMATION_
{
    MemoryBasicInformation,
    MemoryWorkingSetList,
    MemorySectionName,
    MemoryBasicVlmInformation
} MEMORY_INFORMATION_CLASS;
/*--------------------------------------------------------------------------*/ 
typedef struct _CLIENT_ID 
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
/*--------------------------------------------------------------------------*/ 
typedef struct _USER_STACK 
{
    PVOID FixedStackBase;
    PVOID FixedStackLimit;
    PVOID ExpandableStackBase;
    PVOID ExpandableStackLimit;
    PVOID ExpandableStackBottom;
} USER_STACK, *PUSER_STACK;
/*--------------------------------------------------------------------------*/ 
typedef LONG KPRIORITY;
typedef ULONG_PTR KAFFINITY;
typedef KAFFINITY *PKAFFINITY;
/*--------------------------------------------------------------------------*/ 
typedef struct _THREAD_BASIC_INFORMATION 
{
    NTSTATUS                ExitStatus;
    PVOID                   TebBaseAddress;
    CLIENT_ID               ClientId;
    KAFFINITY               AffinityMask;
    KPRIORITY               Priority;
    KPRIORITY               BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
/*--------------------------------------------------------------------------*/ 
typedef enum _THREAD_INFORMATION_CLASS 
{
    ThreadBasicInformation,
    ThreadTimes,
    ThreadPriority,
    ThreadBasePriority,
    ThreadAffinityMask,
    ThreadImpersonationToken,
    ThreadDescriptorTableEntry,
    ThreadEnableAlignmentFaultFixup,
    ThreadEventPair,
    ThreadQuerySetWin32StartAddress,
    ThreadZeroTlsCell,
    ThreadPerformanceCount,
    ThreadAmILastThread,
    ThreadIdealProcessor,
    ThreadPriorityBoost,
    ThreadSetTlsArrayAddress,
    ThreadIsIoPending,
    ThreadHideFromDebugger
} THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;
/*--------------------------------------------------------------------------*/ 
typedef enum _SYSTEM_INFORMATION_CLASS { SystemHandleInformation = 0x10 } SYSTEM_INFORMATION_CLASS;
/*--------------------------------------------------------------------------*/ 
typedef NTSTATUS (NTAPI *ZwWriteVirtualMemory_t)(IN HANDLE               ProcessHandle,
                                                 IN PVOID                BaseAddress,
                                                 IN PVOID                Buffer,
                                                 IN ULONG                NumberOfBytesToWrite,
                                                 OUT PULONG              NumberOfBytesWritten OPTIONAL);
/*--------------------------------------------------------------------------*/ 
typedef NTSTATUS (NTAPI *ZwCreateProcess_t)(OUT PHANDLE            ProcessHandle, 
                                            IN  ACCESS_MASK        DesiredAccess, 
                                            IN  POBJECT_ATTRIBUTES ObjectAttributes, 
                                            IN  HANDLE             InheriteFromProcessHandle, 
                                            IN  BOOLEAN            InheritHandles, 
                                            IN  HANDLE             SectionHandle    OPTIONAL, 
                                            IN  HANDLE             DebugPort        OPTIONAL, 
                                            IN  HANDLE             ExceptionPort    OPTIONAL);
/*--------------------------------------------------------------------------*/ 
typedef NTSTATUS (WINAPI *ZwQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
                                                      PVOID SystemInformation,
                                                      ULONG SystemInformationLength,
                                                      PULONG ReturnLength);
typedef NTSTATUS (NTAPI *ZwQueryVirtualMemory_t)(IN  HANDLE ProcessHandle,
                                                 IN  PVOID BaseAddress,
                                                 IN  MEMORY_INFORMATION_CLASS MemoryInformationClass,
                                                 OUT PVOID MemoryInformation,
                                                 IN  ULONG MemoryInformationLength,
                                                 OUT PULONG ReturnLength OPTIONAL);
/*--------------------------------------------------------------------------*/ 
typedef NTSTATUS (NTAPI *ZwGetContextThread_t)(IN HANDLE ThreadHandle, OUT PCONTEXT Context);
typedef NTSTATUS (NTAPI *ZwCreateThread_t)(OUT PHANDLE ThreadHandle,
                                           IN  ACCESS_MASK DesiredAccess,
                                           IN  POBJECT_ATTRIBUTES ObjectAttributes,
                                           IN  HANDLE ProcessHandle,
                                           OUT PCLIENT_ID ClientId,
                                           IN  PCONTEXT ThreadContext,
                                           IN  PUSER_STACK UserStack,
                                           IN  BOOLEAN CreateSuspended); 
/*--------------------------------------------------------------------------*/ 
typedef NTSTATUS (NTAPI *ZwResumeThread_t)(IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL);
typedef NTSTATUS (NTAPI *ZwClose_t)(IN HANDLE ObjectHandle);
typedef NTSTATUS (NTAPI *ZwQueryInformationThread_t)(IN HANDLE               ThreadHandle,
                                                     IN THREAD_INFORMATION_CLASS ThreadInformationClass,
                                                     OUT PVOID               ThreadInformation,
                                                     IN ULONG                ThreadInformationLength,
                                                     OUT PULONG              ReturnLength OPTIONAL );
/*--------------------------------------------------------------------------*/ 
static ZwCreateProcess_t ZwCreateProcess = NULL;
static ZwQuerySystemInformation_t ZwQuerySystemInformation = NULL;
static ZwQueryVirtualMemory_t ZwQueryVirtualMemory = NULL;
static ZwCreateThread_t ZwCreateThread = NULL;
static ZwGetContextThread_t ZwGetContextThread = NULL;
static ZwResumeThread_t ZwResumeThread = NULL;
static ZwClose_t ZwClose = NULL;
static ZwQueryInformationThread_t ZwQueryInformationThread = NULL;
static ZwWriteVirtualMemory_t ZwWriteVirtualMemory = NULL;
/*--------------------------------------------------------------------------*/ 
#define NtCurrentProcess() ((HANDLE)-1)
#define NtCurrentThread() ((HANDLE) -2)
/* we use really the Nt versions - so the following is just for completeness */
#define ZwCurrentProcess() NtCurrentProcess()     
#define ZwCurrentThread() NtCurrentThread()
#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
/*--------------------------------------------------------------------------*/ 
/* setjmp env for the jump back into the fork() function */
static jmp_buf jenv;
/*--------------------------------------------------------------------------*/ 
/* entry point for our child thread process - just longjmp into fork */
static int child_entry(void) 
{
    longjmp(jenv, 1);
    return 0;
}
/*--------------------------------------------------------------------------*/ 
static BOOL haveLoadedFunctionsForFork(void)
{
    HANDLE ntdll = GetModuleHandle("ntdll");
    if (ntdll == NULL) return FALSE;

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }

    ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll, "ZwCreateProcess");
    ZwQuerySystemInformation = (ZwQuerySystemInformation_t) GetProcAddress(ntdll, "ZwQuerySystemInformation");
    ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t) GetProcAddress(ntdll, "ZwQueryVirtualMemory");
    ZwCreateThread = (ZwCreateThread_t) GetProcAddress(ntdll, "ZwCreateThread");
    ZwGetContextThread = (ZwGetContextThread_t) GetProcAddress(ntdll, "ZwGetContextThread");
    ZwResumeThread = (ZwResumeThread_t) GetProcAddress(ntdll, "ZwResumeThread");
    ZwQueryInformationThread = (ZwQueryInformationThread_t) GetProcAddress(ntdll, "ZwQueryInformationThread");
    ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t) GetProcAddress(ntdll, "ZwWriteVirtualMemory");
    ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose");

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }
    else
    {
        ZwCreateProcess = NULL;
        ZwQuerySystemInformation = NULL;
        ZwQueryVirtualMemory = NULL;
        ZwCreateThread = NULL;
        ZwGetContextThread = NULL;
        ZwResumeThread = NULL;
        ZwQueryInformationThread = NULL;
        ZwWriteVirtualMemory = NULL;
        ZwClose = NULL;
    }
    return FALSE;
}
/*--------------------------------------------------------------------------*/ 
int fork(void) 
{
    HANDLE hProcess = 0, hThread = 0;
    OBJECT_ATTRIBUTES oa = { sizeof(oa) };
    MEMORY_BASIC_INFORMATION mbi;
    CLIENT_ID cid;
    USER_STACK stack;
    PNT_TIB tib;
    THREAD_BASIC_INFORMATION tbi;

    CONTEXT context = {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT};

    if (setjmp(jenv) != 0) return 0; /* return as a child */

    /* check whether the entry points are initilized and get them if necessary */
    if (!ZwCreateProcess && !haveLoadedFunctionsForFork()) return -1;

    /* create forked process */
    ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, NtCurrentProcess(), TRUE, 0, 0, 0);

    /* set the Eip for the child process to our child function */
    ZwGetContextThread(NtCurrentThread(), &context);

    /* In x64 the Eip and Esp are not present, their x64 counterparts are Rip and 
    Rsp respectively.
    */
#if _WIN64
    context.Rip = (ULONG)child_entry;
#else
    context.Eip = (ULONG)child_entry;
#endif

#if _WIN64
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp, MemoryBasicInformation, &mbi, sizeof mbi, 0);
#else
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp, MemoryBasicInformation, &mbi, sizeof mbi, 0);
#endif

    stack.FixedStackBase = 0;
    stack.FixedStackLimit = 0;
    stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize;
    stack.ExpandableStackLimit = mbi.BaseAddress;
    stack.ExpandableStackBottom = mbi.AllocationBase;

    /* create thread using the modified context and stack */
    ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess, &cid, &context, &stack, TRUE);

    /* copy exception table */
    ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation, &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof tbi, 0);
    ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress, &tib->ExceptionList, sizeof tib->ExceptionList, 0);

    /* start (resume really) the child */
    ZwResumeThread(hThread, 0);

    /* clean up */
    ZwClose(hThread);
    ZwClose(hProcess);

    /* exit with child's pid */
    return (int)cid.UniqueProcess;
}
/*--------------------------------------------------------------------------*/

مختصر مايقوم به أنه ينشئ عملية جديدة وينسخ حالة مكدس ومسجلات المعالج للعملية الأب ويعدل مؤشر الـeip لتبدأ الدالة البرنامج من بعد إستدعاء fork.

مثال :

#include <stdio.h>
#ifdef _WIN32
#include "forkWindows.h"
#endif

int main(int argc, char **argv)
{

    int p = fork();

    /* لا أعتقد أن الدالة ستعيد أي عدد سالب */
    if( p < 0 ) {
        perror("Error?");
        return -1;
    }
    else if(p == 0) {

        /* الطريقة الوحيدة لكي تتأكد من أن العملية موجودة */
        FILE *fh = NULL;
        if( (fh = fopen("/Users/Barakat/Desktop/child.txt", "w")) == NULL )
            return -1;

        fprintf(fh, "I'm the child");
        fclose(fh);
    }
    else {
        printf("I'm the main process\n");
    }



    return 0;
}

التصريف :

gcc winfork_example.c forkWindows.c

الا أنه ينقصه أشياء كثيرة مثل بناء الـenvironment variables ومسار المجلد الحالي, العملية الإبن لاتعرف مسارها ولاتعلم ماتأثيرها على برنامج وأيضاً لاتتهي العملية بل تبقى مجمدة لفترة ولا أعلم إذا كان هناك أيضاً شيء آخر ناقص. من العصر وأنا أحاول تعديه الا أن مخي صدع ولم أكتب سطر مفيد.

نصيحة أن تريح نفسك من محاولة تقليد fork وتستخدم الـthreading, فهي عكس ماذكرت أكثر كفاءة بأضعاف من fork والتعامل معها وإدارتها ومشاركة البيانات بين أكثر من thread أسهل من مشاركتها بين عملتين. بالنسبة لقولك " فعند وجود خطأ فسوف يغلق البرنامج تماماً", الخطأ لن يحدث الا لوجود علة في البرنامج وحسنت fork أن العملية الأم لن تنهار بإنهيار عملية إبن. لكن إنهيار عملية أو خيط معالجة يعني وجود علّة في البرنامج وأنت مضطر لإصلاحها في كلا الحالتين.

نصيحة أن تستخدم pthread فهي متوفّرة على ويندوز بإستخدام mingw-gcc ولن تضطر لتعديل أي جزء من الكود. مثال عليها (ستجد شرح سريع هنا وشرح مفصل في كتاب Linux Programming Interface) :

#include <stdio.h>
#include <pthread.h>

void *threaded_func(void *data);

int main(int argc, char **argv)
{
    pthread_t thread[10];
    unsigned int i;
    int status;

    for( i = 0 ; i < 10 ; i++ ) {
        status = pthread_create(&thread, NULL, threaded_func, (void *) i);
        if( status != 0 ) {
            perror("pthread_create");
        }
    }

    for( i = 0 ; i < 10 ; i++ )
        pthread_join(thread, NULL);

   return 0;
}

void *threaded_func(void *data)
{
    printf("Thread # = %d\n", (int) data);
    return NULL;
}

التصريف على كلا النظامين :

gcc pthread_example.c -lpthread

وإذا أردت نشر البرنامج على ويندوز تأكد من إرفاق ملف الـdll الذي يأتي مع mingw مع برنامجك. غالباً مايكون على المسار :

C:\MinGW\bin\pthreadGC2.dll
2
#3

الحكاية أنني قمت بكتابة مكتبة أتعامل معها بواسطة Qt أستطيع إستخدامها لإنشاء برامج تعمل كـ Web serves و بالفعل أستخدم في تصميمها Threads و لكن عند وجود أي علة في أي Class من التي يتم إستدعائها بواسطة المكتبة فسوف ينهار البرنامج.

قمت بقراءة بعض الوثائق الخاصة بطريقة تصميم Apache و وجدت أنه يعتمد في تشغيل PHP على حالتين:

1- تشغيل PHP كـ module وهنا ستعمل بنظام Thread و لها فوائد كثيرة مثل مشاركة البيانات و الحفاظ على موارد الجهاز و لكن نظام الأمن فيها أضعف و عند وجود أي عله في محرك PHP أو مكتبة من مكتبات PHP فسوف ينهار Apache فوراً و هذا جربتة بنفسي و أعاني منه في الإستضافات.

2- تشغيل PHP كـ CGI و بالطبع هنا ستعمل PHP بنظام Process و ستحافظ على عدم إنهيار Apache و لكن تستهلك الكثير من الموارد و بها حماية أكبر من الحالة الأولى.

لم أكتفي بذلك و حملت ngenx و بدأت قرأة الكود (لقلة الوثائق) و كيفية عملة و وجدته يتبع اسلوب واحد و هو CGI في تشغيل PHP

من الواضح أنني سوف أكتفي بإنشاء المكتبة لتعمل على لينوكس أو سأستسلم و أكمل بإستخدام Threads وده إحتماله أكبر بكثير لأن البرامج ستكون كبيرة و ستستهلك الذاكرة sad.gif

بالنسبة لـ try و catch فهل هناك حل حتى تستطيع الإمساء بالأخطاء بعيداً عن إستخدام throw فلو كان هناك مثلاً معادلة تم فيها القسمة على صفر (و لم ألاحظ ذلك فالكمال لله smile.gif) فسوف يتم إنهاء البرنامج حتى لو كنت أستخدم try فهي ترى throw فقط؟

إذا وجد حل أو لم يوجد حل لـ try فهل هناك كتاب لكيفية إستخدام LLVM لإنشاء JIT خاص بي (الموارد ضعيفة قوييييييييييييي) ؟

شكراً لك أخي Mr.B

1
forum-signature-01.gif
#4

ربما تود الإطلع على هذه المقالة ولمعرفة الفرق بين الـforking والـthreading.

الـcgi إسلوب قديم لإنشاء تطبيقات الويب, أعتقد أنه باقي فقط لأنه معيار. مشكلته أنه مع كل طلب فالبرنامج , مفسر الـphp مثلاً, يبدأ العمل ويغلق مع إنتهاء الطلب وهذا يستهلك قدرات المعالج, حتى لو كنت أنت المستخدم الوحيد فستلاحظ البطئ. في mod_php مثلاً يحمّل مفسر php مرّة واحدة مع بدء تشغيل أباتشي ولايغلق الا بإغلاق أباتشي (لاينشئ أباتشيء thread بل يقوم بعمل forking).

يبدو لي أن مشلتك فقط مع معالجة الأخطاء, إذا حصل خطأ في الخادم فلست مضطر لإيقاف كامل العملية. يُمكنك إنشاء دالة ترسل رد بوجود "خطأ في الخادم" - 500 Internal Server Error- ونظف خلف العملية فإن خصصت ذاكرة حررها وإن فتحت مقبض أغلقه ثم بعدها أوقف الـthread فقط , إيقاف الـthread لايوقف العملية الأم. أيضاً هذه الطريقة تتيح لك فرصت تسجيل الخطأ وإصلاحة لاحقاً.

2

مواضيع مشابهة