2012年8月16日星期四

编程语言中的时间戳

如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)?
Javatime
JavaScriptMath.round(new Date().getTime()/1000)
getTime()返回数值的单位是毫秒
Microsoft .NET / C#epoch = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000
MySQLSELECT unix_timestamp(now())
Perltime
PHP time()
PostgreSQL SELECT extract(epoch FROM now())
Python先 import time 然后 time.time()
Ruby获取Unix时间戳:Time.now 或 Time.new
显示Unix时间戳:Time.now.to_i
SQL ServerSELECT DATEDIFF(s, '1970-01-01 00:00:00', GETUTCDATE())
Unix / Linuxdate +%s
VBScript / ASPDateDiff("s", "01/01/1970 00:00:00", Now())
其他操作系统
(如果Perl被安装在系统中)
命令行状态:perl -e "print time"
如何在不同编程语言中实现Unix时间戳(Unix timestamp) → 普通时间?
JavaString date = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new java.util.Date(Unix timestamp * 1000))
JavaScript先 var unixTimestamp = new Date(Unix timestamp * 1000) 然后commonTime = unixTimestamp.toLocaleString()
Linuxdate -d @Unix timestamp
MySQLfrom_unixtime(Unix timestamp)
Perl先 my $time = Unix timestamp 然后 my ($sec, $min, $hour, $day, $month, $year) = (localtime($time))[0,1,2,3,4,5,6]
PHPdate('r', Unix timestamp)
PostgreSQLSELECT TIMESTAMP WITH TIME ZONE 'epoch' + Unix timestamp) * INTERVAL '1 second';
Python先 import time 然后 time.gmtime(Unix timestamp)
RubyTime.at(Unix timestamp)
SQL ServerDATEADD(s, Unix timestamp, '1970-01-01 00:00:00')
VBScript / ASPDateAdd("s", Unix timestamp, "01/01/1970 00:00:00")
其他操作系统
(如果Perl被安装在系统中)
命令行状态:perl -e "print scalar(localtime(Unix timestamp))"
如何在不同编程语言中实现普通时间 → Unix时间戳(Unix timestamp)?
Javalong epoch = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").parse("01/01/1970 01:00:00");
JavaScriptvar commonTime = new Date(Date.UTC(yearmonth - 1, dayhour,minutesecond))
MySQLSELECT unix_timestamp(time)
时间格式: YYYY-MM-DD HH:MM:SS 或 YYMMDD 或 YYYYMMDD
Perl 先 use Time::Local 然后 my $time = timelocal($sec, $min, $hour, $day, $month, $year);
PHPmktime(hourminuteseconddaymonthyear)
PostgreSQLSELECT extract(epoch FROM date('YYYY-MM-DD HH:MM:SS'));
Python先 import time 然后 int(time.mktime(time.strptime('YYYY-MM-DD HH:MM:SS', '%Y-%m-%d %H:%M:%S')))
RubyTime.local(yearmonthdayhourminutesecond)
SQL ServerSELECT DATEDIFF(s, '1970-01-01 00:00:00', time)
Unix / Linuxdate +%s -d"Jan 1, 1970 00:00:01"
VBScript / ASPDateDiff("s", "01/01/1970 00:00:00", time)

2011年3月30日星期三

在Linux下如何创建自己的函数库

想建个函数库,把自己常用的一些函数包括进去,在使用时include一下就行了,应该怎么构建?另外,我看到常用的一些函数库,如math.h,里面只有一些函数的声明,那么执行部分在哪里?又是如何实现调用的?初学编程,谢谢各位了! 

我自己以前写的一点东西.可能对你有用吧! 

如何为Linux增加库

一. 静态库
在Linux下的静态库是以.a为后缀的文件。
1. 建静态库
h1.c 源文件
#include <stdio.h>
void hello1()
{
printf(“the first hello!\n”);
}
h2.c 源文件
#include <stdio.h>
void hello2()
{
printf(“the second hello!\n”);
}
2.主程序
hello.c 源文件
int main()
{
hello1();
hello2();
return 0;
}
输入命令:
gcc –c h1.c
gcc –c h2.c
ar –r libhello.a h1.o h2.o
ar –s libhello.a
ranlib libhello.a
最后再
gcc –static hello.c –L. –lhello –o hello即可生成可执行文件。注意要使用-static参数,否则生成的仍然是动态类型的文件,不过对于hello这个库则是采用静态方式来使用的而已。

二. 动态库
1.建动态库
#include <stdio.h>
void hello1()
{
printf(“the first hello!\n”);
}
h2.c 源文件
#include <stdio.h>
void hello2()
{
printf(“the second hello!\n”);
}
2. 主程序
hello.c 源文件
int main()
{
hello1();
hello2();
return 0;
}
输入命令:
gcc –fPIC –g –c h1.c –o libh1.o
gcc –fPIC –g –c h2.c –o libh2.o
gcc –g –shared –W1 –o libh1.so libh1.o –lc
gcc –g –shared –W1 –o libh2.so libh2.o –lc
然后再将主程序与库相连进行编译
gcc –g hello.c –o hello –L. –lh1 –lh2
最后再将当前路径放到库查找路径中去
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATY
最后再执行./hello即可运行。 

三. 动态库的查找过程
一旦连接器完成了自己的初始化工作,就查找程序所需要的库的名字。程序头中有一个指针指向dynamic段,它包含了动态连接的信息,dynamic段中的DT_STRTAB指向一个字符表,而其中的DT_NEEDED包含了一个相对于字符表的偏移,指向所需要的库名。
对于每一个库,连接器首先查找库文件位置,从本质上说是一个相当复杂的过程。DT_NEEDED所描叙的库文件名一般类似libXt.so.6 (Xt开发包, 版本6),库文件可能在任意的库文件目录中,还也可能有重名的文件。在我的系统中,这个库的实际文件名是/usr/X11R6/lib/libXt.so.6.0,最后的“.0”表示次版本号。

连接器查找下列几个地方:

l 首先查看 .dynamic 段是否包含了一个叫DT_RPATH的项(它是一个以冒号分隔的库文件搜索目录列表)。这个项是在程序被连接器连接时,由命令行开关或者环境变量添加上去的。它常应用于子系统中,比如像数据库应用,我们要装载一些程序集合以及支持库到一个目录中去的时候。

l 查看是否存在环境变量 LD_LIBRARY_PATH(它是一个以冒号分隔的库文件搜索目录列表)。这个项可以帮助开发者建立一个新版本的库,把他的路径添加到LD_LIBRARY_PATH中,把它和现存的可连接程序一同使用,用来测试新的库,

l 连接器查看库高速缓存文件 /etc/ld.so.conf ,它包含了库名和路径的一个对应列表,如果库名存在,连接器就使用它对应的路径,用这个查找方法能够找到大部分的库(文件名不需要和要求完全符合,这点可以参考接下来的“库的版本”)。

l 如果上叙的查找都失败,连接器就查找默认路径 /usr/lib ,如果库文件依旧没有找到,则显示一个错误然后退出。

连接器找到了库文件后,先打开它,然后读取ELF头,找到指向各个段的指针。连接器为库的代码段和数据段分配空间并映射到内存,随后是bss(不分配空间)。.通过库的 .dynamic 段,连接器添加这个库的符号表到符号表链,如果库所依赖的其它库没有装载的话,则添加那个库到装载队列中。

完成这个过程后,所有的库都已经被映射,loader在逻辑上拥有了一个全局的符号表,它是全部程序和被映射库的符号表的联合。

2010年10月19日星期二

《GNU/LINUX环境编程》-套接字编程的一个注解

书籍:《GNU/LINUX环境编程》(第二版)

出版社:清华大学出版社

备注:中文版

原书第157-158页代码:
源代码如下:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

#define MAX_BUFFER 128
#define DAYTIME_SERVER_PORT 13

int main(void)
{
int serverFD,connetctionFD;
struct sockaddr_in servaddr;
char timebuffer[MAX_BUFFER+1];
time_t currentTime;

serverFD = socket ( AF_INET, SOCK_STREAM, 0 );

memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(DAYTIME_SERVER_PORT);

bind( serverFD, (struct sockaddr *)&servaddr, sizeof(servaddr) );

listen( serverFD, 5 );

while(1) {
connetctionFD = accept( serverFD, (struct sockaddr *)NULL, NULL );

if (connetctionFD >= 0) {
currentTime = time( NULL );
snprintf( timebuffer, MAX_BUFFER, "%s \n", ctime( &currentTime) );
write( connetctionFD, timebuffer, strlen(timebuffer));
close( connetctionFD );
}
}
}

按照书上的讲解,使用的端口为13,

但是运行的效果是:
hello@world:~$ telnet localhost 13
Trying ::1...
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
hello@world:~$

很显然这个端口是被占用的了。

所以为了能够有效运行,将宏定义中的端口改为1024之后的不常用的端口,就可以了。
如果还是不行的话,就更换端口。(或者事先用工具扫描已经使用的端口,分配端口时,避开就可以了)

2010年10月8日星期五

socket网络编程:IO复用_select函数

摘要:在UNIX系统编程过程中,一个进程往往需要等待多个描述字发生某一事件,如可读、可写或异常等等。进程不能永远地等待其中任何单独一个描述字,它需要同时等待所有描述字,此时就是IO复用技术,系统调用select就是实现这一目标的方式之一。本文详细介绍select函数。

select广泛应用于各种场合,因为select对于任何描述字都有作用,它被应用网络程序,也被应用于终端程序,也被应用其它场合。下面我们先来看看它的函数原型:

#include<sys/select.h>
#include<sys/time.h>
int select(int max_fd_p_1, fd_set *readset, fd_set* writeset, fd_set* exceptset, struct timeval* timeout);
返回:就绪描述字的个数,0——超时,-1——出错

select函数有5个参数,我们会介绍每一个参数。

参数max_fd_p_1
内核需要一个数字来指定最大的描述字,因为内核使用这个参数来遍历一组描述字。又因为描述字是从0开始计数的,所以max_fd_p_1实现上是最大描述字的加上1的值。
参数readset
select可以测试一组描述字是否可读,用户需要告诉内核所有关心的描述字,readset就是用来指定关心的描述字集合。
参数writeset
如同readset指定可读描述字集合一样,writeset用来指定关心的可写描述字集合。
参数exceptset
跟readset、writeset一样,exceptset用来指定所关心的异常描述字集合,也就是当集合中一个描述字出现异常时会得到内核的一个通知。
参数timeout
用来指定超时的时长,这是一个结构体:
struct timeval
{
     long tv_sec;//秒数
     long tv_usec;//微秒数
}
这个结体定义在头文件sys/time.h中。

上面我们简单地针对select自身进行了说明。我们注意到,select的参数有4个是指针,如果这些指针取值是空的话会如何?对于指定描述字集合的参数,如果取空的话,它们的意义很直接,就是没有相应的关心的描述字集合。比如readset==NULL,则说明用户不关心任何一个描述字是否可读,对于writeset、exceptset也完全一样。但是对于timeout==NULL的情况就是很直接的,它表示永远等待下去,此时我们不关心需要等待多长时间,我们只要求至少有一个描述字满足用户所关心的。

对于timeout里面两个成员都取值0的时候,相应意义很直接,就是等待0秒0微秒,也就是说不等待任何时间。此时就相当于简单的轮询。

上面我们介绍了最后一个参数的,那么先前的三个参数我需要注意一下,就是它们的类型是fd_set,fd_set是什么样的类型呢?我并不知道,但有一点,就是POSIX标准为我们提供了四个宏,这四个宏可以完成我们需要对fd_set的操作,而fd_set的实际类型留给系统去定义。这四个宏分别是:

void FD_ZERO(fd_set* set);//把set设置为0
void FD_SET(int fd, fd_set* set);//从把fd添加到set
void FD_CLR(int fd, fd_set* set);//从set中删除fd
int FD_ISSET(int fd, fd_set* set);//判断fd是否在set中被设置
使用这四个宏,我们就可以完成fd_set相关的所有操作了。

select是一相十分复杂的函数,它的返回值如上所说,-1为错误,0为超时,正数为就绪的打字个数。但是事实上打字的个数往往很小,系统会一个限制,一般为1024;这是因为fd_set这个类型的限制,同时select也不应该处理过多的打字,否则话会有性能问题。

select也会设置errno值,最常见的值可能就是EINT,表示一个信号中断了select调用,作为应用程序应该在select返回错误的时候查看errno。


来源:
博爱声明: 

本文档在《博爱协议(版本一)》下授权,更多内容请查看:博爱
请关心支持人类的慈善事业,把爱与帮助给予需要她们的儿童与贫困家庭。
请关注如下站点:
联合国儿童基金会 | 中华慈善总会

网络编程socket之服务器编程的挑战

摘要:网络服务器编程是一个巨大的挑战,这个挑战来自服务器需要完成的任务太多,这个挑战来自可能存在的恶意或无意破坏,这个挑战来自大量的与内核的交互,同时这个挑战也来自网络本身的极端不稳定性。在面对这些挑战时,服务器却被要求为快速、稳定、可用的结合物!本文关注这些挑战,并讨论如何避免其中遇到的一些问题。

让我们逐一来讨论上面说到的问题,首先说说服务器要完成的任务太多。现在的服务器大多非常繁忙,比如每秒都可以需要处理上千上万个客户请求,这对于稍等大一点网站或者游戏服务器来说都是经常的事。此时我们有两个思路来解决这个问题,一个思路是提高硬件的速度,另一个思路就是提高软件的性能,通常情况是两路齐走。本文不说硬件,所以只是软件这条思路如何走。

为了提高软件性能当然是算法,此时我必须使用并行的算法来处理,并行是当前提高恨不能的必要方法,也是基本方法。在一台主机上的并行是通过软件来实现的,此时有进程模式和线程模式,由于现代的操作系统已经很强大了,所以我们还可以依赖操作系统提供的IO复用、异步IO等等功能实现隐匿的并行。当然后者简单易于实现,但性能的提升空间却没有手工并行再加上此类技术大,所以人们都会手工实现线程或进程,然后在线程或进程内部再使用IO复用、异步IO等技术,所谓为性能无所不用其极。

服务器编程的另一个挑战是需要识别有意无意的破坏,或把有意无意的破坏限制在最小的范围内,单一客户的请求应该是独立与其他用户的请求的,此时一个用户的失败不至于影响其他用户,从而把这种失败破坏限制在有限的范围内。服务器最常见的破坏就是拒绝服务器攻击,如果没有此种特性的服务器,面对拒绝服务攻击只能听之任之了。有时服务器可能需要维护一个统计信息,这样可以比较容易识别出有意破坏,但这一思路可以提高服务器的可用性的同时却降低了服务器的速度。对,服务器的三个特性有时是相互矛盾的,我们提高了其中一个,相应的另一个却在下降,此时需要一种权衡,或更复杂的决择机制。分层或分级是此时我没用的策略,在不同的级别上采用不同的决择。并行在此时仍然是基本的思路与措施。



来源:
博爱声明: 

本文档在《博爱协议(版本一)》下授权,更多内容请查看:博爱
请关心支持人类的慈善事业,把爱与帮助给予需要她们的儿童与贫困家庭。
请关注如下站点:
联合国儿童基金会 | 中华慈善总会

网络编程socket之close与shutdown函数

其对于服务器进程而言,因为一个进程可以同时打开的连接是有限的,如果不在某个时候主动终止已有的连接,那么对于服务器进程来说,它总会在某个时候因为无法打开新连接而失败。

对于UNIX系统而言,无论是一般的文件描述符,还是网络中使用的套接字都是描述字的范围,所以它们都可以用close函数来完成关闭的任务,然后对于网络套接字这一个特殊的描述字,我们却可以使用更加丰富的shutdown函数完成有选择的关闭。下面我们先来看看这个两个函数:

#include<unistd.h>
int close(int fd)
返回:0——成功, -1——失败

#include<sys/socket.h>
int shutdown(int sockfd, int howto)
返回:0——成功, -1——失败

让我们来回忆一下,一个文件描述符关联着一个实际的文件——不管这个文件是什么,普通文件或网络套接口等等,但是多个打字可以同时与一个文件关联,并且内核维护一个文件引用计数。正常情况下,close函数不武断地释放一个描述字关联的文件,除了这个引用计数为0的时候,并且无论如何,当对一个描述字调用了close函数,用户无法再次使用这个描述字。这是close相对shutdown的两点差别,相应地shutdown是针对socket套接口定制的函数,所以它会做的更好。

shutdown函数不是参考引用计数,它会直接关闭相应的socket套接口,无论引用计数是多少。我们还知道,socket套接口是全双工的,也就是用户可以读,也可以写。存在一个这样的情况,此时用户已经把所有要写的数据都写完了,他想告诉对等端这一点;或者用户把所有要读的数据都读完成了,同样要告诉对等端。此时就是关闭读这一半或写这一半,使用shutdown可以完成这一个。系统定义了3个宏,这3个宏分别用作shutdown的后一个参数:

  • SHUT_RD:关闭读这一半,此时用户不能再从这个套接字读数据,这个套接口接收到的数据都会被丢弃,对等方不知道这个过程。
  • SHUT_WR:相应地关闭写这一半,此时用户不能再向套接字中写数据,内核会把缓存中的数据发送出去,接着不会再发送数据,对等端将会知道这一点。当对等端试图去读的时候,可能会发生错误。
  • SHUT_RDWR:关闭读与写两半,此时用户不能从套接字中读或写。它相当于再次调用shutdown函数,并且一次指定SHUT_RD,一次指定SHUT_WR。

刚才我写完了服务器编程之fork并行模式,这个文章中我们使用close函数,试问一下:我们可以使用shutdown函数代替吗?简单的思考之后,我们知道不可以使用shutdown函数代替,因为我们在子进程中只是想解除sockfd与那个监听套接口的关联,并不想释放这个套接口,原因是在父进程还要使用它;相应在父进程我也只是想解除cfd与其套接口的关联,我们在子进程还需要使用cfd。从这个例子中可以看到,close与shutdown有各自的用处,并不能相互代替,就算在socket套接字这一特定情况下也如此。


来源:
博爱声明: 

本文档在《博爱协议(版本一)》下授权,更多内容请查看:博爱
请关心支持人类的慈善事业,把爱与帮助给予需要她们的儿童与贫困家庭。
请关注如下站点:
联合国儿童基金会 | 中华慈善总会

服务器编程之fork并行模式

摘要:网络编程本身并不复杂或多么困难,复杂或困难的是在不稳定的网络中做到稳定的服务器与通信,此时并行技术是实现的稳定的服务器的技术之一,当然也不是服务器端使用的技术。fork是POSIX系统中产生新进程的唯一方法,可以实现进程并行编程模式。

在介绍服务器编程的fork模式之前,我们首先来说说fork自身的问题,对于深知fork的读者可以跳过本段内容。fork通常作为一个系统调用来存在,或者作为一个系统调用的简单封装,它对于内核来说只做一件事:把当前进程的内存镜像复制一份,以在系统中产生一个新进程,并在两个进程中各返回一次,新产生的进程与原本的进程完全相同,它们共享着文件描述符,如此在两个进程中可以对同一文件进行操作,套接字也如此。

服务器编程的fork并行模式的基本的思想是:一个进程监听一相应的端口,当取得一个客户连接的时候,它使用fork产生一个新进程,由于新进程是原进程的复本,所以它可以与客户完成通信,而原来的父进程仍然继续监听那个端口,下面是一个典型的代码片断:

#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
        //如同串行的程序,这之前完成许多初始工作
        //下面代码假设sockfd是一个监听套接字

        while(1)
        {
                //如果想知道客户地址,后面两个参数可以指定相应的结构
                int cfd=accpet(sockfd, NULLNULL);
                int pid=fork();
                if(0>pid)
                {
                        //创建进程失败,我们直接退出服务器
                        //这只是一个示例程序
                        return -1;
                }
                else if(0<pid)
                {
                        close(cfd);
                        continue;
                }

                //此处代码我意在让新进程运行,此时pid==0是明显的
                close(sockfd);
                
                //新进程使用cfd与客户进程进行通信
                //在通信结束之后,新进程也许就直接退出了
        }
}

上述的代码片断只是一个示例性的,以简单明了为基础,但它却直接而不明白反应了fork并行模式的基本思想。在实际中的程序还可以需要对信号进行处理,尤其当新进程比父进程早退出的时候,父进程会收到一个SIGCHILD信号,如果父进程不wait或waitpid的话,它将变成一个僵死进程,显然这是不希望的。


来源:
博爱声明: 

本文档在《博爱协议(版本一)》下授权,更多内容请查看:博爱
请关心支持人类的慈善事业,把爱与帮助给予需要她们的儿童与贫困家庭。
请关注如下站点:
联合国儿童基金会 | 中华慈善总会