-
用对象关系去思考 LEFT JOIN 的用法
2007-10-30 02:07:30
有这样两个表-- custom 和 address 表。
对于每个customer, 有0个到多个地址。对于地址,本来是一定有customer的,但是这里加入一个非法的地址。address用外键cid关联到customer。
这个语句:select custom.*, address.address from custom left join address on address.cid = custom.cid;
相当于找出所有的customer, 然后对应找出每个customer的address--select了customer以后,然后把address乘过来。
select custom.*, address.address from custom left join address on address.cid = custom.cid;
1|custom1|addr1 of c1
1|custom1|addr2 of c1
2|custom2|
3|custom3|addr1 of c3
3|custom3|addr2 of c3
4|custom4|addr1 of c4
而相反的结果是:
sqlite> select custom.*, address.address from address left join custom on address.cid = custom.cid;
1|custom1|addr1 of c1
1|custom1|addr2 of c1
3|custom3|addr1 of c3
3|custom3|addr2 of c3
4|custom4|addr1 of c4
||missing custom, invalid address
则是对于每个地址,去找customer。
测试时候所用的所有数据如下:
sqlite> .schema custom
CREATE TABLE custom( cid int,
cname varchar(32));
sqlite> select * from custom;
1|custom1
2|custom2
3|custom3
4|custom4
sqlite> .schema address
CREATE TABLE address(aid int, cid int, address text); - - sqlite保留了我创建时候的格式..
sqlite> select * from address;
1|1|addr1 of c1
2|1|addr2 of c1
3|3|addr1 of c3
4|3|addr2 of c3
5|4|addr1 of c4
6|0|missing custom, invalid address
后记:这个测试的动机和部分内容来源于SQLObject开发文档。 -
[论坛] 用C来实现base64编码与解码
2007-07-31 14:44:37
base64编码是邮件传输中比较常用的一种简单编码,它把8bit的编码,转换成6bit有效的编码,头两个bit置零(和邮件传输网关有关,高位为1会被过滤),因为只有6bit有效,所以有64个码,另外,base64希望编码能在ascii范围内,以便打印出来,所以会设置一个码表,编码映射到码表来。
也就是说,base64把每3个8bit,转换成4个8bit,其中每个8bit里面的高2bit是恒0。
这个是码表:CODE:
static const char *codes =我写了一个编码和解码的程序,根据rfc来写的,也许因为我理解的漏洞,或者有逻辑错误,程序不一定是正确的(但是我测试多多个图片和email文本的编码,是正确的), 请用者自己检查正确性。
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
编码程序:CODE:
// 输入串,输入串长,输出串,输出串长。解码过程需要弄一个编码反查表:
void base64_encode(char *in, const int in_len, char *out, int out_len)
{
int base64_len = 4 * ((in_len+2)/3); // 要保证输出串的长度。
assert(out_len >= base64_len);
char *p = out;
int times = in_len / 3;
for(int i=0; i<times; ++i) {
*p++ = codes[(in[0] >> 2) & 0x3f];
*p++ = codes[((in[0] & 0x3) << 4) + (in[1] >> 4)];
*p++ = codes[((in[1] & 0xf) << 2) + (in[2] >> 6)];
*p++ = codes[in[2] & 0x3f];
in += 3;
}
// pad .. 如果不够3个8bit来,后面要补充'=',rfc里面称为pad
if(times * 3 + 1 == in_len) {
*p++ = codes[(in[0] >> 2) & 0x3f];
*p++ = codes[((in[0] & 0x3) << 4)];
*p++ = '=';
*p++ = '=';
}
if(times * 3 + 2 == in_len) {
*p++ = codes[(in[0] >> 2) & 0x3f];
*p++ = codes[((in[0] & 0x3) << 4) + (in[1] >> 4)];
*p++ = codes[((in[1] & 0xf) << 2)];
*p++ = '=';
}
*p = 0;
}CODE:
char ords[128];这样可以根据明文,把编码位置查出来,然后把多余的00都去掉,最后的一个反pad过程要注意一下下
#define PAD -1
void init_ords()
{
ords['A'] = 0; ords['B'] = 1; ords['C'] = 2; ords['D'] = 3; ords['E'] = 4;
ords['F'] = 5; ords['G'] = 6; ords['H'] = 7; ords['I'] = 8; ords['J'] = 9;
ords['K'] = 10; ords['L'] = 11; ords['M'] = 12; ords['N'] = 13; ords['O'] = 14;
ords['P'] = 15; ords['Q'] = 16; ords['R'] = 17; ords['S'] = 18; ords['T'] = 19;
ords['U'] = 20; ords['V'] = 21; ords['W'] = 22; ords['X'] = 23; ords['Y'] = 24;
ords['Z'] = 25; ords['a'] = 26; ords['b'] = 27; ords['c'] = 28; ords['d'] = 29;
ords['e'] = 30; ords['f'] = 31; ords['g'] = 32; ords['h'] = 33; ords['i'] = 34;
ords['j'] = 35; ords['k'] = 36; ords['l'] = 37; ords['m'] = 38; ords['n'] = 39;
ords['o'] = 40; ords['p'] = 41; ords['q'] = 42; ords['r'] = 43; ords['s'] = 44;
ords['t'] = 45; ords['u'] = 46; ords['v'] = 47; ords['w'] = 48; ords['x'] = 49;
ords['y'] = 50; ords['z'] = 51; ords['0'] = 52; ords['1'] = 53; ords['2'] = 54;
ords['3'] = 55; ords['4'] = 56; ords['5'] = 57; ords['6'] = 58; ords['7'] = 59;
ords['8'] = 60; ords['9'] = 61; ords['+'] = 62; ords['/'] = 63;
ords['='] = PAD;
}CODE:
void base64_decode(char *in, const int in_len, char *out, int *out_len)我写的文件,仅仅考虑了自己测试的需要,没有考虑通用性,把主要实现发上来供讨论。
{
int decode_len = in_len * 3 / 4;
assert(*out_len > decode_len);
char tmp[in_len]; // for ords[]
char *p = in;
for(int i=0; i<in_len; ++i,++p) {
tmp[i] = ords[*p];
}
char *q = out;
p = tmp;
*out_len = 0;
for(int i=0; i<in_len-4; i+=4) {
*q++ = (p[0] << 2) + (p[1] >> 4);
*q++ = (p[1] << 4) + (p[2] >> 2);
*q++ = (p[2] << 6) + p[3];
p += 4;
*out_len += 3;
}
// deal with pad
if(p[3] != PAD) { // no pad
*q++ = (p[0] << 2) + (p[1] >> 4);
*q++ = (p[1] << 4) + (p[2] >> 2);
*q++ = (p[2] << 6) + p[3];
*out_len += 3;
} else if(p[2] != PAD) { // one pad
*q++ = (p[0] << 2) + (p[1] >> 4);
*q++ = (p[1] << 4) + (p[2] >> 2);
*out_len += 2;
} else if(p[1] != PAD) { // two pads
*q++ = (p[0] << 2) + (p[1] >> 4);
*q++ = (p[1] << 4);
*out_len += 2;
}
*q++ = 0;
}
base64.rar
(2007-07-31 14:43:54, Size: 8.71 kB, Downloads: 0) -
[论坛] 给BlackBerry充电的模块
2007-07-12 01:36:08
新买了台bb,在linux上默认充不了电,因为bb需要500mA的电流。
于是找了个驱动。是2.6.21内核新增的,不过我在2.6.19上测试通过(实际上这个代码的想法来自barry项目--可以上sf去找,这个项目在很早的内核就有了)。
这些天都考试,今天又想找工作了,没有时间给代码上注释。明天或者后天我上来的时候,补上注释
CODE:
#include <linux/kernel.h>Makefile可以这样写:
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#define RIM_VENDOR 0x0fca
#define BLACKBERRY 0x0001
static int debug;
#ifdef dbg
#undef dbg
#endif
#define dbg(dev, format, arg...) \
if (debug) \
dev_printk(KERN_DEBUG , dev , format , ## arg)
static struct usb_device_id id_table [] = {
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY) },
{ }, /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static int magic_charge(struct usb_device *udev)
{
char *dummy_buffer = kzalloc(2, GFP_KERNEL);
int retval;
if (!dummy_buffer)
return -ENOMEM;
/* send two magic commands and then set the configuration. The device
* will then reset itself with the new power usage and should start
* charging. */
/* Note, with testing, it only seems that the first message is really
* needed (at least for the 8700c), but to be safe, we emulate what
* other operating systems seem to be sending to their device. We
* really need to get some specs for this device to be sure about what
* is going on here.
*/
dbg(&udev->dev, "Sending first magic command\n");
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0xa5, 0xc0, 0, 1, dummy_buffer, 2, 100);
if (retval != 2) {
dev_err(&udev->dev, "First magic command failed: %d.\n",
retval);
return retval;
}
dbg(&udev->dev, "Sending second magic command\n");
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0xa2, 0x40, 0, 1, dummy_buffer, 0, 100);
if (retval != 0) {
dev_err(&udev->dev, "Second magic command failed: %d.\n",
retval);
return retval;
}
dbg(&udev->dev, "Calling set_configuration\n");
retval = usb_driver_set_configuration(udev, 1);
if (retval)
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
return retval;
}
static int berry_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
dbg(&udev->dev, "ower is set to %dmA\n",
udev->actconfig->desc.bMaxPower * 2);
/* check the power usage so we don't try to enable something that is
* already enabled */
if ((udev->actconfig->desc.bMaxPower * 2) == 500) {
dbg(&udev->dev, "device is already charging, power is "
"set to %dmA\n", udev->actconfig->desc.bMaxPower * 2);
return -ENODEV;
}
/* turn the power on */
magic_charge(udev);
/* we don't really want to bind to the device, userspace programs can
* handle the syncing just fine, so get outta here. */
return -ENODEV;
}
static void berry_disconnect(struct usb_interface *intf)
{
}
static struct usb_driver berry_driver = {
.name = "berry_charge",
.probe = berry_probe,
.disconnect = berry_disconnect,
.id_table = id_table,
};
static int __init berry_init(void)
{
return usb_register(&berry_driver);
}
static void __exit berry_exit(void)
{
usb_deregister(&berry_driver);
}
module_init(berry_init);
module_exit(berry_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");CODE:
#Makefile驱动里面命令2对7290貌似是有点错误的。我有空也调试一下。
obj-m := berry_charge.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
make -C $(KERNELDIR) SUBDIRS=$(PWD) modules -
[论坛] 用dante+pam_mysql搭建socks5代理
2007-06-22 11:56:51
dante是一个很好的socks代理,同时有服务器和客户端的功能,现在我只说服务器部分。
pam是linux的通用认证模块,可以ls一下/etc/pam.d/,看看内容就明白了。
pam_mysql: http://pam-mysql.sourceforge.net/Documentation/install.php?seemore=y , 用于把pam认证的信息,映射到mysql里面,方便web存取。
我主要参考了参考文章[1],这里给出我的配置。
1.需要安装的软件 dante(后面我附上一个archlinux的PKGBUILD),mysql,pam_mysql。
2. mysql的数据表:
表 logs:
+---------+--------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+-------------------+-------+
| msg | varchar(255) | YES | | NULL | |
| user | varchar(50) | YES | | NULL | |
| pid | int(10) | YES | | NULL | |
| host | char(32) | YES | | NULL | |
| rhost | char(32) | YES | | NULL | |
| logtime | timestamp | NO | | CURRENT_TIMESTAMP | |
+---------+--------------+------+-----+-------------------+-------+
这个表,我发现应该是没有作用的,不过建立也没关系。
表 user:
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| username | varchar(50) | YES | | NULL | |
| password | varchar(30) | YES | | NULL | |
| comment | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+
用于存放用户数据,密码的加密方式,情看后面的配置。
3. pam_mysql 的配置:
建立 /etc/pam.d/sockd 文件,内容:auth required /lib/security/pam_mysql.so user=sockd passwd=8123196 host=localhost db=sockd table=user usercolumn=username passwdcolumn=password crypt=0 logtable=logs logmsgcolumn=msg logusercolumn=user logpidcolumn=pid loghostcolumn=host logtimecolumn=logtime
account required /lib/security/pam_mysql.so user=sockd passwd=8123196 host=localhost db=sockd table=user usercolumn=username passwdcolumn=password crypt=2 logtable=logs logmsgcolumn=msg logusercolumn=user logpidcolumn=pid loghostcolumn=host logrhostcolumn=rhost logtimecolumn=logtime
上面的内容,指定了认证的项目,pam_mysql的路径,mysql存取用户,以及最表和字段到认证字段的映射。
4. /etc/sockd.conf 的内容:
internal: eth0 port = 1080 # 内网入口
external: eth0 # 出口网卡
method: username none pam # 认证方式
user.notprivileged: nobody
logoutput: /var/log/danted.log
connecttimeout: 30
# 通过的客户端
client pass {
from: 192.168.0.0/16 to: 0.0.0.0/0
log: connect disconnect # 记录的内容
}
# 阻止其他客户端
client block {
from: 0.0.0.0/0 to: 0.0.0.0/0
log: connect error
}
# 允许的数据流
pass {
from: 0.0.0.0/0 to: 0.0.0.0/0 port gt 0
command: bind
log: connect disconnect
method: pam # 这里指定pam认证,然后/etc/pam.d/sockd指定pam认证的方式
}
pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
protocol: tcp udp
log: connect disconnect
method:pam
}
我对内网进行用户认证,如果需要ip认证,直接指定单独的client pass就行了。
5. mysql添加新用户
grant select,update,insert on sockd.* to sockd@localhost identified by "8123196"
完毕了,请提意见
参考:
[1] http://www.360doc.com/showWeb/0/0/251512.aspx
[2] man sockd
[3] man sockd.conf -
Linux下监视进程运行状况(一)
2006-08-05 07:26:57
-----------------------
下面是发在论坛上面的原文。
转来这里的时间是8月5日。因为论坛上根本没人看:(
我没学过latex,文章的格式仅仅是传统ubb的格式。
我想文章标题以后可能需要改动,因为文章的内容以后不但会涉及程序运行时资源的监视,也会包括控制部分。
希望看客喜欢。
-----------------------
[说在前面]
我想这个主题对很多初涉Linux开发的人都有帮助。
我本身也是刚接触Linux下开发的新手,说的不对,请大家指正。
以连载的形式分3次左右写完,想到哪写哪,都是经验总结。因为时间有限,一次的内容不会很多。
我发现找不到合适的板块来发这个主题,本文章的分类应该是“开发 -> Unix系统编程”。
本文作者:Fluke at Ospattern(dot)net
作者背景:在校计算机本科生,Linux使用者,C程序开发者,研究都不深。
本章属性:可以分发,没有版权,文中引用代码,也许有部分和某些程序相同,但我不认为是侵权,因为代码本来就有固定的模式,也许是我学习之后,忘了故意去修改什么。如果侵犯了您的权力,请与我联系,并诚挚请您原谅。
[正文]
Linux下监视一个程序的运行状况,有不同的方法,适用范围也不大一样。我知道的一些方法如下:
1. Process accounting
这个是新内核(kernel 2.x.x都支持)支持的一个功能,不过在编译的时候,需要Enable它,默认是没有打开的。打开之后,可以通过几个命令来获取正在运行的程序的CPU Time, Memory占用等等信息。
优点,信息全面,操作也容易。缺点,需要重新编译内核,很多用户不希望这样,尤其是现在很多使用标准化的发行版的用户都不希望去改变系统的默认设定。
2. Top, ps, cat /proc/pid/...
我们还可以通过top, ps命令来或者直接cat /proc/pid(这里pid是系统分配给程序的一个id,运行时会在/proc目录下有相应的资源文件,不是本文的重点,本人对此也十分不熟悉,仅仅是知道)目录下面的文件查看系统正在运行的程序,和实时的系统资源占用。
优点,方便,缺点,对于需要跟踪某进程的运行情况的用户,这种方法就不适用了。
3. 通过在子进程来运行需要监视的程序,通过标准的GNU C函数库来获取子进程资源
这种是本文要讲述的方法。优点显而易见,能从头到尾跟踪程序的运行,不但能获取其资源状况,还能控制其运行,比如在程序试图获取更多系统资源(CPU时间或者内存等)时发送信号使其结束。
那么,我们从什么地方开始呢?
gnu time。一个很小巧的计算程序运行时间的程序,也许很多人都用过。我们创建一个小程序(当然你也可以不使用自己创建的程序,直接统计一下你系统上已经有的程序,比如find),然后用gnu time来统计一下。以后在计算其他资源的时候,我们还会修改这个程序,使其占用一定的内存或者什么的,以达到我们的测试要求。OK,我们来创建一个c++程序(我只会这个),叫做AccountMe.cpp。
下面是AccountMe.cpp的内容:[ DISCUZ_CODE_34 ]然后,用g++来编译程序,我这里gcc的版本是4。命令如下:[ DISCUZ_CODE_35 ] 好了,看看这个程序会占用多少时间,输入如下命令:[ DISCUZ_CODE_36 ] 然后你会看到,类似下面的统计:[ DISCUZ_CODE_37 ] 上面程序运行的原理是,time接受第一个参数argv[1]得到要运行的进程的路径,然后fork()出一个子进程,子进程要做的事情是通过execvp()系列函数运行程序argv[1],父进程要做的事情有几件:1. 得到开始时间start_time。2.在waitpid()系列函数,等待子进程,直到结束,或者直到自己设置的限制以后,终止子进程。3.得到结束时间end_time。最后计算花费时间run_time = end_time - start_time。
很简单的过程,不是么?
在本篇的最后,我仅仅想和大家分享一下怎么在C里面创建子进程,怎么样运行外部进程。其他的,留待本系列的后续文章。至于C语言里面资源的统计,读者可以参开fork,wait,limit等manpage,以及google上查找详细资料,本文不打算充当这方面的知识普及者(本人并没有研究透彻,不好扩展下去),要用到的东西,会在用的时候简单介绍,或者直接用例子说话。
这里来看看一段运行子进程的代码,代码本身只充当运行者的身份,出了获取被运行者的返回值之外,什么也不做。
ProcRunner.cpp的内容:[ DISCUZ_CODE_38 ] 编译上面的程序,用来运行刚才我们创建的AccountMe,然后我们来看看程序运行出错和正常退出的情况。下面是编译的命令:[ DISCUZ_CODE_39 ] 运行AccountMe:[ DISCUZ_CODE_40 ] 这里要用绝对路径,因为我们的程序里面没有添加处理路径的功能(这个留待以后讨论,在这里并不重要)。
因为程序正常退出,所以我们看到的情况如下:[ DISCUZ_CODE_41 ] 现在我们给AccountMe一些错误,看看ProcRunner有什么反应。当然,你也可以用signal去结束掉AccountMe来玩玩;)
我简单的使AccountMe返回-1,然后运行ProcRunner。发现返回值变了:[ DISCUZ_CODE_42 ] 发现,ProcRunner捕获的错误号码是255而不是-1,这是因为错误号码不是简单的是错误程序的返回值,不过包含了返回值在其二进制位中,这次不讨论。
好了,这部分已经结束,我花了一个多小时来写,写完之后搜索了一下网上的文章,发现我的文章也许没有必要存在,不过没有关系,有人喜欢的话,我会继续往下写:)
------------
Tue 01 Aug 2006 04:19:42 AM CST fluke on Archlinux
-
Fluke's Blog
2006-08-02 07:00:08
标题搜索
我的存档
数据统计
- 访问量: 1988
- 日志数: 7
- 建立时间: 2005-10-16
- 更新时间: 2007-10-30
