2012年7月

由《黑镜》看人工智能

看了黑镜,挺有感触的,特别是第二集的剧情。我们是幸运的,没有活在要用木头生火的时代,不过更幸运的是我们没有生活在人工智能无处不在的时代。原以为人工智能是未来的趋势,不过确实是肯定的。人工智能会在不久的将来完全走进我们的生活,改变我们的生活,然后会改变很多人的命运。不久之前看到一篇博客,说的是“工作输给机器人以后”,二十世纪初的马,在英国算是最流行的交通具,几乎无处不在。不久之后汽车的出现,就代替了马,新鲜事物的出现必然导致旧事物的消失。事物这个词的概念很宽泛,汽车的出现代替了马车,马就消失了。不久人工智能的盛行,会出现无人驾驶的汽车,google和丰田合作的无人驾驶系统就证实了,人工智能源于人,但是远比人高级。那么这个系统的出现,淘汰的是什么?淘汰的是司机!也许人工智能的出现可能会导致大规模的失业,然后社会就会出现分层。然后就会出现黑镜里面的现象,所有的人活在虚拟的世界里,活在一个看似很先进,全都是人工智能的世界。剧中描述,每个人都活在一个四周都是镜子的房间里面,镜子可以呈现出各种各样的虚拟世界,所有的日常生活需要都可以通过手势来实现,甚至连挤牙膏都是人工智能的,钞票作为一般等价物已经淘汰了,取而代之的是虚拟分值。社会出现了分层,普通的人每天的工作就是不断骑自行车(赚取分值用于虚拟消费),为各式各样的人工智能工具发电(作者为什么不用机器人来取代人力汽车发电?我想如果把最后底层人能干的活都取代了,这世界就乱了)。其实里面的人就活在一个黑匣子里面,然后努力的骑车赚取虚拟分值,然后生活在更大的黑匣子里面,每天都穿着一样的衣服,做一样的事情。剧中的男主角为了让心爱的人摆脱这种生活,花了1500W分值让她去参加“达人秀”,结果女主角上台前被吃了药物,被“洗脑”了。最后在虚假的评委和一大堆“幸灾乐祸”的虚拟观众呼喊下,同意去拍A片。之后男主角看穿了这个世界,痛心疾首,省吃俭用,努力骑车,赚够1500W分值重回“达人秀”。在台上,以自杀威胁,把所有在人工智能世界存在的虚假,做作,以及他对“达人秀”的仇恨,对虚拟世界的绝望都喊了出来。现场鸦雀无声,原以为男主角这一顿大骂会让人们都醒悟。没想到,评委说他的表演很好,很逼真。更讽刺的是,男主角在评委的利益诱惑下屈服了,去做了一个电台播音员的小丑。最后,女主角送给男主角的手工做的企鹅也消失了,取而代之的是机器的企鹅。

达尔文的生物进化论是真理,人工智能的发展也许会淘汰很多很多。

Stopped (tty output)

问题描述:


在一个shell脚本里,执行了需要I/O操作的程序,一旦把这个shell脚本放在后台执行,就会出现这个错误!

问题原因:


This signal is most commonly generated when a process cannot write to the controlling terminal

because it has been placed into the process background

问题解决:


网上说可以用 ./script < /dev/null &,但是我的脚本需要交互,所以还是挂了。

accept()函数

今天在优化Web服务器的时候发现了一个问题,抓包的时候看了下,这是chrome处理的过程,发起的连接

一看已经有很多次三次握手了,那不是有很多个连接了。但是我的web服务器是单线程的啊?并且是阻塞

的,按理说每次都应该阻塞在accept那里,为什么会建立这么多次连接了呢?

仔细想想,协议栈里面是存在一个请求队列的,listen()函数就是把请求的socket放到队列中,而accept呢就

是从队列中取出一个socket,然后返回套接字的标识,该sock就从队列中删除了。所以上面wireshark抓出来

的包有这么多次连接是正常。

Linux下十六进制转成中文

在实现web服务器一项功能中:在处理post包数据段的内容是以一个字节一个字节读取的,所以如果post的

内容是中文的话,输出的时候会变成十六进制,那么怎么把十六进制再转成中文呢?

#include 
#include 
#include 

void ansiToutf8(char *src,char *desStr);

int main()
{
	char desStr[200]= {'\0'};
	char *p = "%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%EF%BC%88%E7%AC%AC%E4%B8%89%E7%89%88%EF%BC%89";	
	ansiToutf8(p,desStr);
	return 0;
}

void ansiToutf8(char *src,char *desStr)
{
	if(*src == '%')		//是中文
	{
		int des[200] = {0};
		int i = 0,j = 0;
		while(*(src+i) != '\0')
		{
			char buf[4] = {'\0'};		
			strncpy(buf,src+i,3);
			printf("%s\n",buf);
			sscanf(buf,"%%%x",&des[j]);
			printf("%d\n",des[j]);
			sprintf(desStr,"%s%c",desStr,des[j]);
			i = i + 3;
			++j;
		}
		printf("%s",desStr);
	}
}

转换后的结果就是
$ 程序设计(第三版)

awk基础学习

awk 处理流的形式也是一行一行的,读取一行然后按照指定的模式进行处理,处理完成后默认输出到终端。

awk [-F fild:separator] 'command' filename
[-F fild-separator ]是可选的,awk默认以空格作为缺省的分隔符号,在脚本中可以通过FS=“X”来设定,X是任意分割符。
表达式匹配的特殊字符
\ ^ $ . [ ] | ( ) * + ?

awk 内置变量


ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk 浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行- F 选项
NF 浏览记录的域个数,用$NF可以表示最后一个域
NR 已读的记录数
OFS 输出域分隔符,OFS="#",输出分隔符就是#
ORS 输出记录分隔符
RS 控制记录分隔符

awk的正则表达式在//里面,模式匹配的sed差不多
找到所有含有root的行,打印第一个域
awk -F : '/root/{print $1}' /etc/passwd

找到行首是root的行,打印第一个域
awk -F : '/^root/{print $1}' /etc/passwd

用~来进行字段匹配,第一个域是root的行
awk -F : '$1~/root/' /etc/passwd
找到第一个域不是root的行
awk -F : '$1!~/root/' /etc/passwd

用print的时候,不同参数之间用逗号作分割符号,则输出以空格作为分割符号;若以空格作为参数分割符号,输出被不存在分隔符号
echo "etc/fire/test.c" | awk -F / '{print $1,$2,$3}'  ==>> home fire test.c
echo "etc/fire/test.c" | awk -F / '{print $1 $2 $3}'  ==>> homefiretest.c

开始结束都加hello,中间打印第一个域
awk -F: 'BEGIN{print "......hello.......\n"} {print $1} END{print ".........hello........\n"}' /etc/passwd

使用if语句进行匹配


找出第一个域是root的行,注意不能写成=,如果写出=,那么就把所有行的第一个域都赋值为root,所有行都匹配
awk -F: '{if($1=="root")} print $0' /etc/passwd

找出第三个域值是32的行
awk -F: '{if($3=="32") print $0}' /etc/passwd

不等于
找出第一个域不是root的行
awk -F: '{if($1!="root") print $0}' /etc/passwd


找出第一个域是root并且第五个域也是root的行
awk -F: '{if($1=="root"&&$5=="root") print $0}' /etc/passwd

NF:每行记录域的总个数
awk -F: '{print NF}' /etc/passwd

NR:已读记录域的数目
awk -F: '{print NR}' /etc/passwd

每行首加总域数和已读域数
awk -F: '{print NF,NR,$0}' /etc/passwd

在保证一行的域大于0,一个域是root的情况下,打印所在行号和行
awk -F: '{if(NF>0 && $1=="root") print NR,$0}' /etc/passwd

显示当前目录的名字,用$NF来显示最后一个域
pwd | awk -F / '{print $NF}'
显示一个目录的文件名字
echo "/home/firefoxbug/test.c" | awk -F / '{print $NF}'

找到第一个域是root行,并把第三个域加3,第四个域加4
可以用{}对匹配的行进行操作,若要执行多条语句,用;分割
awk -F: '{if($1=="root"){$3=$3+3;$4=$4+4;print $0}}' /etc/passwd

在每行最后添加一个域,域值是第三个域值和第四个域值的和
awk -F: '{$(NF+1)=$3+$4;print $(NF)}' /etc/passwd

统计当前目录下所有文件长度,首先排除目录,然后求得ls -l 的第五个域总和
ls -l | grep '^[^d]' | awk '{print $5;total=total+$5} END{print total}'

awk的printf函数,printf函数用法和C语言类似
awk 'BEGIN{printf ("%s\n","hello")}'

awk命令中传递参数
awk '模式' 变量=值 filename
比如查看 df -k 第四个域大于TARGET的项,其中TARGET是用户指定的
df -k | awk '{ if($4>TARGET) {printf ("%s : %d\n",$6,$4) } }' TARGET=650000

查看/etc/passwd下第一个域是USER的行,其中USER由用户指定
awk -F : '{if ($1==USER) {printf ("HAVE %s\n",$1) }}' USER=root /etc/passwd

setenv,getenv,fork

我们在实现CGI程序的时候,有时候会用setenv设置环境变量传递给子进程。那么父进程是怎么传递给子进程的呢?

 
[cc lang="c"]
//father.c
#include
#include
#include
#include
#include

extern char **environ;

int main()
{
char *str = "Hello From Father";
char *emptylist[] = { NULL, };
char *filename = "./child";

if(fork() == 0)
{
setenv("QUERY_STRING",str,1);
if (execve(filename, emptylist, environ) < 0)
{
printf("Execve error");
}
}
wait(NULL);
return 0;
}
[/cc]

[cc lang="c"]
//child.c
#include
#include
#include

int main()
{
printf("this is in son process\n");
char *p2 = (char *)getenv("QUERY_STRING");
if ( p2 )
printf("%s\n",p2);

return 0;
}
[/cc]
extern char **environ
这句话指向的就是环境变量的字符串数组。

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。要是没有extern就会无法在子进程中获取环境变量。下面看一个environ获取环境变量获取的例子:
[cc lang="c"]
#include
#include
#include
#include
#include

extern char **environ;

int main()
{
char **p = environ;
while (*p != NULL)
{
printf("%s (%p)\n", *p, *p);
*p++;
}
return 0;
}
[/cc]
另外还要注意的一点就是,在setenv设置环境变量的时候是不能随便设置的,比如你要设置HOLLO=“hello”,这是不行的。为什么呢?调用fork产生子进程的时候,子进程会做一份父进程环境变量的拷贝,因为前面说了,拷贝就是通过environ来实现的,而environ是已经定义好的了。你要是非想用setenv传递环境变量,那么就只能用系统的环境变量来作为临时交换进行传递。

cgi下dup2()问题

函数名: dup2
功 能: 复制文件句柄
用 法: int dup2(int oldhandle, int newhandle);

#include <unistd.h>

define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */

问题描述:


在写一个小型web服务器的过程中,把返回的socket描述符sockfd用dup2函数复制给标准输出描述符,这样才调用cgi程序的时候对于所有原本要输出到标准输出的流全部都会写入到sockfd,返回给客户端。但是有一个问题:就是我想用lsz来传输一个文件,客户端一直没有返回。我查看了下进程,发现一直都卡在lsz里面的。我想了很久,发现 lsz 发送文件的过程中会有输出,这时候打输出就默认输出到sockfd里面了,导致我程序死了。也就是说怎么样实现cgi程序里,在需要和终端交互的时候实现交互?

代码:


/*************server.c*****************/
setenv("QUERY_STRING", cgiargs, 1); 			
Dup2(fd, STDOUT_FILENO);         /* Redirect stdout to client */		//把标准输出的句柄复制到文件句柄
Execve(filename, emptylist, environ); /* Run CGI program */

Execve里面执行的程序就是lsz,用于向串口传输文件。

问题解决:


cgi程序里面,主要做法:把不需要传回给sockfd的输出返回给终端就行了,那么就不会写到客户端的sockfd里面了。那么怎么实现呢?可以这样:因为在这个时候sockfd==STDOUT_FILENO,只需要把STDOUT_FILENO先保存下来oldsockfd,然后用open重新打开终端,然后用dup2把返回的描述符和STDOUT_FILENO联系在一起,这个时候输出又重定向到STDOUT_FILENO去了,然后输出结束,再用dup2把oldsockfd又和STDOUT_FILENO联系在一起。这时候输出又重定向到sockfd去了,下面是代码:
int oldsockfd = STDOUT_FILENO;
int ttyfd = open("/dev/tty",O_RDWR);
dup2(ttyfd,STDOUT_FILENO);
//do somethings 
dup2(STDOUT_FILENO,oldsockfd);