之前写过一篇文章,说明通过实现C/C++条件编译方式是实现水电站监控系统跨平台特性最合适的方式。这里我再补充说明其中的技术重点,其实也就是Unix/Linux与windows采用C/C++编程中需要注意的区别。
1. 文件访问相关函数
文件访问函数在不同的操作系统上存在一些行为细节方面的不同,如调用fread时候,windows上文件如果不用rb方式读取, 就会导致遇到空格的时候读取结束,而在Unix/Linux平台上不会出现。此外,在Unix/Linux上可以通过open函数打开文件后,通过read读取信息一般不会遇到问题,但在windows上采用相同调用方法,会发现明明文件中的内容还有很多read函数却没有按照调用入参中的长度限制返回最长的字符串,这可能是由于在windows上read函数中存在一个潜在的缓冲区限制。
2. IPC相关函数
IPC指的是Unix系统进程间访问,常用方式包括共享内存,消息队列和信号量。由于windows系统不支持这些功能,因此通过相近的功能替代。
2.1 共享内存
Unix上共享内存的调用方式基本如下:
shm_id = shmget(SYS_DB_KEY, sizeof(SYS_DB)+i, 0666 | IPC_CREAT)
sysdb = (SYS_DB *)shmat(shm_id, (char *)0, 0)
但在windows上,不存在共享内存的机制,因此必须采用类似的技术代替,最简单的方式即文件映射内存,相同的代码段可以通过下面的方式修改:
sprintf(t_buf, "NCSYS_%d", SYS_DB_KEY);
HANDLE hFileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, sizeof(SYS_DB)+i, t_buf);
sysdb = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0);
2.2 消息队列
Unix环境下,消息队列的实现代码如下:
lan_msg_qid = msgget (LAN_CLIENT_MSG_KEY,0600)
msgsnd (lan_msg_qid,&init_msg,sizeof(init_msg),0))
由于windows上也不存在消息队列,因此采用其他技术实现,比如邮槽,上述代码可以通过下面的方式进行修改:
sprintf(servername, "\\\\.\\Mailslot\\%lld", LAN_CLIENT_MSG_KEY);
lan_mailslot_handle = CreateFile(servername, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
WriteFile(lan_mailslot_handle, &init_msg, sizeof(init_msg),&BytesWritten, NULL)
2.3 信号量
Unix下,信号量的实现代码如下:
1) 初始化:
lan_semid = semget(LAN_SEM_KEY,1, 0600| IPC_CREAT | IPC_EXCL))
2) P操作:
structsembuf sops[1];
sops[0].sem_num = SEM_NUM;
sops[0].sem_op = GET;
sops[0].sem_flg = 0;
semop(lan_semid, sops, 1))
3) V操作:
structsembuf sops[1];
sops[0].sem_num = SEM_NUM;
sops[0].sem_op = RELEASE;
sops[0].sem_flg = 0;
semop(lan_semid, sops, 1)
信号量在windows操作系统中也存在,但是用法和含义与linux和Unix不同,这里按照windows信号量的用法改写代码,具体代码修改方式如下:
1) 初始化:
lan_semid = CreateSemaphore(NULL, 0, 1, NULL);
2) P操作:
while(WaitForSingleObject(lan_semid,INFINITE) != WAIT_OBJECT_0);
3) V操作:
ReleaseSemaphore(lan_semid,1,NULL);
3. 网络访问
网络访问在windows操作系统中也存在,但是用法与linux和Unix不同,这里按照windows网络访问的用法改写代码。
- 调用gethostbyname()等属于WinSock API库的函数前,必须要调用WSA-Startup函数,因此在所有程序执行前,直接增加WSA-Startup语句
- 没有信号函数,所以不能使用sigset(SIGALRM, alarm_handler);通过alarm函数实现定时器功能。用event替代,代码如下:
DWORD NumberOfBytesRead;
OVERLAPPED ov;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
- 没有gethostent,需要实现类似的代码。
- windows没有poll函数,使用windows完成端口代替。
4. 其他区别
- 字节对齐方式不同,导致结构体的大小不同,需要在include定义中对结构体进行补字节的操作.
- sleep函数不同参数不同,unix下sleep后面参数单位为秒,windows下Sleep函数参数单位为毫秒
- windows不支持fork创建新进程,改成CreateProcess,并且需要增加一个exe来实现子进程行为,之前子进程的相关参数需要以参数方式带入子进程。
- 没有gettimeofday,在windows平台上自行实现