Cygwin知识集锦
Cygwin是一个用于Windows操作系统下的类Linux环境。包括以下组件:
- cygwin1.dll:一个提供大量POSIX系统调用功能的模拟层,位于Cygwin程序与Windows操作系统之间。目前有32/64位版本支持Windows XP3之后的所有NT系统。cygwin1.dll提供的API尽可能的遵守UNIX规范、Linux实践。与Linux不同,Cygwin使用的C标准库是newlib而不是glibc
- 一系列的工具集,提供类似Linux的行为,例如命令、类库等
当第一个基于Cygwin的进程创建时,cygwin1.dll被加载到其代码段,并创建共享内存区域、全局同步对象。这些区域和对象被所有Cygwin进程共享(虽然他们有各自的cygwin1.dll实例),可以用于跟踪打开的文件描述符,以及辅助fork、exec调用。每个Cygwin进程具有包含PID、UID、信号掩码的进程结构。
Cygwin与MinGW虽然都会被作为GNU工具链看待,但是二者在本质上是不同的。
Cygwin在某种程度上可以看作一个虚拟的操作系统层(如上所述,由cygwin1.dll驱动),在其上运行的应用程序可以使用绝大部分POSIX接口,这些程序感觉自己就在Linux上运行一样。
MinGW虽然也提供了POSIX风格的接口,但是这些接口的实现都是被对接到Win32 API上的。MinGW编译的程序是纯粹的Win32应用程序,因此也无法支持一些Linux下特有的系统调用,例如fork()。
Windows下的Linux虚拟机可以直接运行Linux应用程序,而Cygwin则不能运行,程序必须从源代码重新编译。Cygwin程序是标准的Windows PE格式。
由于Windows本身的限制,Cygwin无法保持POSIX兼容性,但是这样的情况比较少见。
Cygwin提供的POSIX兼容性和Windows API并非水乳交融,因此如果在应用中混用POSIX调用、Windows调用,可能导致意外结果。特别的,Cygwin信号与阻塞的Windows函数不能一起使用。
Cygwin同时支持POSIX、Win32风格的路径。传递给DLL的路径被转换为Win32格式;在Cygwin应用看来,文件系统是POSIX兼容的。
从1.7.0开始,/etc/fstab中包含了Windows文件系统到POSIX文件系统的映射关系。如果不进行默认设置,将进行类似这样的映射: C:\Windows -> /cygdrive/c/Windows
Windows NT包含了一个基于ACL的安全模型。在支持此ACL的文件系统(NTFS)上,Cygwin将Win32文件所有权、权限映射到ACL。chmod调用将UNIX文件权限映射到Win32等价的权限上。
具有管理员权限的用户可以chown文件所有者。
Cygwin 1.1.3引入了设置Real UID、Effective UID的机制。
Cygwin中的fork调用没有很好的映射到Win32 API上。fork()调用本身的语义要求父子进程的地址空间布局完全一致,而Windows本身没有提供在进程间拷贝地址空间的机制,此外Windows下的一些特性可能破坏fork()实现的可靠性,这导致了一些流行问题:
- DLL基址冲突:与类UNIX系统使用“位置无关代码”的共享库不同,Windows共享库假设了一个固定的基址。一旦同一个进程加载的两个DLL的基址冲突了,Windows就要将其中一个“rebase”到不同的地址上,这种rebase行为具有不一致性:可能每次rebase的DLL不同,移动到的目的位置也不同。Cygwin可以为动态加载的DLL补充前述的不一致性。但是对于静态链接的DLL(statically-linked)之间的冲突,是在cygwin.dll加载之前就被Windows处理的,Cygwin无能为力,只能通过rebaseall工具处理
- Windows Vista开始引入的地址空间布局随机化(ASLR),允许线程栈、堆、内存映射文件、静态链接DLL随机的定位到进程的地址空间。此行为会干涉fork()的正常行为。如果不可移动对象(进程堆、系统DLL)被定位在错误的位置,Cygwin无法进行补偿行为
总之,现有的Windows实现导致无法实现完全可靠的fork(),偶发性的fork()失败不可避免。
当Cygwin进程启动后,会有一个辅助线程被用于信号的处理,该线程等待Windows事件,并将其转换为信号,当进程收到信号后,会根据信号bitmask的设置进行合适的处理。大部分的UNIX信号被支持。
在Cygwin中与Socket相关的调用基本上是调用了Winsock的对应物——MS的Berkeley Socket实现,但是使用了一些技巧。例如,为了允许POSIX信号来中断阻塞调用,所有的Socket调用在底层都是非阻塞的。
由于地址族AF_UNIX在Winsock下不可用,Cygwin利用本地AF_INET代替之,但是这对程序是透明的。
UNIX的select()函数也不能清晰的映射到Win32 API。在Windows下,select()只能用于套接字的处理,而UNIX下可以用于各种类型的文件描述符。
下载setup-x86.exe到安装目录,例如D:\CPP\tools\Cygwin,双击即可安装,设置好安装目录、下载目录后,选择163的镜像即可安装。Cygwin会显示已经port的软件包的列表,根据需要安装即可。
如果想安装基于AMD64的cygwin,可以下载setup-x86_64.exe,但是64位的Cygwin中,类库和工具没有32位的全面。
软件包 | 说明 | ||
apt-cyg | 安装此软件后,不需要每次都打开setup-x86.exe进行软件安装,只需要使用类似于apt-get方式即可安装:
注:目前Cygwin已经修改了散列算法,对于apt-cyg 0.95,应该修改343行的md5sum为sha512sum |
||
ncurses | 与Terminal使用相关的小工具,例如clear命令 | ||
cygserver | 该工具自动安装,需要运行 /usr/bin/cygserver-config进行必要的配置。cygserver用于支持以“服务”默认来运行Cygwin应用程序。配置步骤如下:
完毕后,在Windows系统服务中可以看到一个“CYGWIN cygserver”,启动即可。 |
||
Devel目录 | 开发人员必备的软件包都在该目录下,对于C/C++开发,需要AutoTools(autoconf、automake)、binutils、cmake、cygwin-devl、gcc-core、gcc-g++、gdb、libtool、make | ||
Cygwin/X | 在Cygwin下运行基于X Windows的GUI应用,例如gedit,需要用到,安装步骤:
|
||
OpenSSH | 安装头SSH服务要求cygserver被安装。步骤如下:
默认的用户密码就是当前Windows的用户密码 |
||
输入法 |
在Cygwin/X下使用输入法,推荐ibus-pinyin,需要安装的包包括:ibus、ibus-pinyin、dconf(不安装的话配置无法生效)。 安装完毕后,设置ibus为自动启动(以MATE为例:System - Perferences - Startup Applications 添加“/usr/bin/ibus-daemon -d”) |
在Cygwin下使用gcc,与在Linux下的用法一样,下面是编译、运行HelloWorld的例子:
1 2 3 |
gcc hello.c -o hello.exe #注意后缀 hello.exe #打印:Hello, World |
64位的Cygwin工具链默认使用微软x64调用协定。可以使用32位的Cygwin工具链、Win32 API来构建应用程序。
有一点需要注意的,64位Cygwin使用了和Windows、MinGW编译器不同的数据模型。后两者使用LLP64模型,而Cygwin使用与Linux一致的LP64模型。这两个模型一个重要区别是,前者的 sizeof(long) == 4 而后者的 sizeof(long) == 8 (与指针类型、size_t、ssize_t长度一致),该区别会对包括LONG, ULONG, DWORD在内的Win32类型用法产生影响。你不能假设DWORD与ssize_t的长度是一致的4字节整数。
Cygwin内置了X Server,因此可以把GUI英语编译为X applications。此外Cygwin允许你完整的使用Windows的GUI函数。构建GUI应用与命令行应用没有什么区别,只需要添加一个选项: gcc -mwindows 。
DLL即动态链接库,其代码在运行期间动态的链接到应用程序,而不是在编译期间就静态的链接。DLL包括三个组成部分:
- 导出(exports ):包含了DLL允许外部访问的函数、变量的列表,其他部分对外隐藏
- 数据和代码:是DLL作者编写的部分,包括函数、变量,被合并为一整个的目标文件
- 导入库(import library):形式上与UNIX常规的*.a库一样,但是其中仅仅包含了很少的信息,用于告知OS,你的应用程序如何去“导入”DLL,该文件被链接到应用程序
基于最新版本的gcc和binutils,构建一个DLL的过程相当简单,下面是一个例子:
1 2 3 4 5 6 |
#include int hello() { printf("Hello World!\n"); } |
实际应用中,你可能需要更复杂的命令来构建DLL,例如:
1 2 3 4 5 6 7 8 9 10 11 |
#共享库的名字为module,前缀cyg用来将其与Native的Windows DLL区分 gcc -shared -o cyg${module}.dll #libmodule.dll.a为用于导入的库stub -Wl,--out-implib=lib${module}.dll.a #导出所有变量、函数 -Wl,--export-all-symbols -Wl,--enable-auto-import #需要整个捆绑到DLL中的目标文件、静态库 -Wl,--whole-archive ${old_libs} #该DLL需要与之链接的DLL的导入库 -Wl,--no-whole-archive ${dependency_libs} |
如果要在Cygwin中使用已有的DLL,则需要生成Cygwin兼容的导入库,下面是一个示例:
1 2 3 4 5 |
echo EXPORTS > hello.def #列出DLL中的符号,将其输出到文件 nm foo.dll | grep ' T _' | sed 's/.* T _//' >> hello.def #生成导入库 dlltool --def hello.def --dllname hello.dll --output-lib hello.a |
可能是环境变量配置错误,必须在PATH中包含 %CYGWIN_HOME%\bin Eclipse CDT才能识别成功。
Leave a Reply