理解 Proc 文gpȝ |
---|
??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
//////////////////////////////////////////////////////////////////////////////////////
// file_server.c 文g传输序服务器示?br />//////////////////////////////////////////////////////////////////////////////////////
//本文件是服务器的代码
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <stdio.h> // for printf
#include <stdlib.h> // for exit
#include <string.h> // for bzero
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/
#define HELLO_WORLD_SERVER_PORT 6666
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
//讄一个socket地址l构server_addr,代表服务器internet地址, 端口
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); //把一D内存区的内容全部设|ؓ0
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
//创徏用于internet的流协议(TCP)socket,用server_socket代表服务器socket
int server_socket = socket(PF_INET,SOCK_STREAM,0);
if( server_socket < 0)
{
printf("Create Socket Failed!");
exit(1);
}
//把socket和socket地址l构联系h
if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
{
printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
exit(1);
}
//server_socket用于监听
if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
{
printf("Server Listen Failed!");
exit(1);
}
while (1) //服务器端要一直运?br /> {
//定义客户端的socket地址l构client_addr
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
//接受一个到server_socket代表的socket的一个连?br /> //如果没有q接h,q待到有连接请?-q是accept函数的特?br /> //accept函数q回一个新的socket,q个socket(new_server_socket)用于同连接到的客L通信
//new_server_socket代表了服务器和客L之间的一个通信通道
//accept函数把连接到的客L信息填写到客L的socket地址l构client_addr?br /> int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
if ( new_server_socket < 0)
{
printf("Server Accept Failed!\n");
break;
}
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
length = recv(new_server_socket,buffer,BUFFER_SIZE,0);//q里先接收客L发来的要获取的文件名
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
char file_name[FILE_NAME_MAX_SIZE+1];
bzero(file_name, FILE_NAME_MAX_SIZE+1);
strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
// int fp = open(file_name, O_RDONLY);
// if( fp < 0 )
FILE * fp = fopen(file_name,"r");
if(NULL == fp )
{
printf("File:\t%s Not Found\n", file_name);
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
// while( (file_block_length = read(fp,buffer,BUFFER_SIZE))>0)
while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf("file_block_length = %d\n",file_block_length);
//发送buffer中的字符串到new_server_socket,实际是给客户?br /> if(send(new_server_socket,buffer,file_block_length,0)<0)
{
printf("Send File:\t%s Failed\n", file_name);
break;
}
bzero(buffer, BUFFER_SIZE);
} //q段代码是@环读取文件的一D|据,在@环调用sendQ发送到客户端,q里一点的TCP每次接受最多是1024字节Q多了就会分片,因此每次发送时量不要过1024字节?/font>
// close(fp);
fclose(fp);
printf("File:\t%s Transfer Finished\n",file_name);
}
//关闭与客L的连?br /> close(new_server_socket);
}
//关闭监听用的socket
close(server_socket);
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////
// file_client.c 文g传输客户端程序示?br />//////////////////////////////////////////////////////////////////////////////////////
//本文件是客户机的代码
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <stdio.h> // for printf
#include <stdlib.h> // for exit
#include <string.h> // for bzero
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/
#define HELLO_WORLD_SERVER_PORT 6666
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: ./%s ServerIPAddress\n",argv[0]);
exit(1);
}
//讄一个socket地址l构client_addr,代表客户机internet地址, 端口
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr)); //把一D内存区的内容全部设|ؓ0
client_addr.sin_family = AF_INET; //internet协议?br /> client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); //0表示让系l自动分配一个空闲端?br /> //创徏用于internet的流协议(TCP)socket,用client_socket代表客户机socket
int client_socket = socket(AF_INET,SOCK_STREAM,0);
if( client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
//把客h的socket和客h的socket地址l构联系h
if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}
//讄一个socket地址l构server_addr,代表服务器的internet地址, 端口
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
if(inet_aton(argv[1],&server_addr.sin_addr) == 0) //服务器的IP地址来自E序的参?br /> {
printf("Server IP Address Error!\n");
exit(1);
}
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
//向服务器发vq接,q接成功后client_socket代表了客h和服务器的一个socketq接
if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0)
{
printf("Can Not Connect To %s!\n",argv[1]);
exit(1);
}
char file_name[FILE_NAME_MAX_SIZE+1];
bzero(file_name, FILE_NAME_MAX_SIZE+1);
printf("Please Input File Name On Server:\t");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
//向服务器发送buffer中的数据
send(client_socket,buffer,BUFFER_SIZE,0);
// int fp = open(file_name, O_WRONLY|O_CREAT);
// if( fp < 0 )
FILE * fp = fopen(file_name,"w");
if(NULL == fp )
{
printf("File:\t%s Can Not Open To Write\n", file_name);
exit(1);
}
//从服务器接收数据到buffer?br /> bzero(buffer,BUFFER_SIZE);
int length = 0;
while( length = recv(client_socket,buffer,BUFFER_SIZE,0)) //循环接收Q再写到文g
{
if(length < 0)
{
printf("Recieve Data From Server %s Failed!\n", argv[1]);
break;
}
// int write_length = write(fp, buffer,length);
int write_length = fwrite(buffer,sizeof(char),length,fp);
if (write_length<length)
{
printf("File:\t%s Write Failed\n", file_name);
break;
}
bzero(buffer,BUFFER_SIZE);
}
printf("Recieve File:\t %s From Server[%s] Finished\n",file_name, argv[1]);
close(fp);
//关闭socket
close(client_socket);
return 0;
}
integers(整数)~码为:i<整数>e
开始标记iQ结束标Cؓe
例如Q i1234e 表示为整?234
i-1234e 表示为整?1234
整数没有大小限制
i0e 表示为整?
i-0e 为非?br />?开头的为非法如Q?i01234e 为非?/p>
lists(列表)~码为:l<bencoding~码cd>e
开始标Cؓl,l束标记为e
列表里可以包含Q何bencoding~码cdQ包括整敎ͼ字符Ԍ列表Q字典?br />例如Q l4:test5abcdee 表示Z个字W串["test","abcde"]
dictionaries(字典)~码为d<bencoding字符?gt;<bencoding~码cd>e
开始标Cؓd,l束标记为e
关键字必Mؓbencoding字符?br />值可以ؓMbencoding~码cd
例如Q d3:agei20ee 表示为{"age"=20}
d4:path3:C:\8:filename8:test.txte 表示为{"path"="C:\","filename"="test.txt"}
具体文gl构如下Q?br />全部内容必须都ؓbencoding~码cd?br />整个文gZ个字典结?包含如下关键?br />announce:tracker服务器的URL(字符?
announce-list(可?:备用tracker服务器列?列表)
creation date(可?:U子创徏的时_Unix标准旉格式Q从1970 1??00:00:00到创建时间的U数(整数)
comment(可?:备注(字符?
created by(可?:创徏人或创徏E序的信?字符?
info:一个字典结构,包含文g的主要信息,为分二种情况Q单文gl构或多文gl构
单文件结构如下:
length:文g长度Q单位字?整数)
md5sum(可?Q长32个字W的文g的MD5校验和,BT不用这个|只是Z兼容一些程序所保留!(字符?
name:文g?字符?
piece length:每个块的大小Q单位字?整数)
pieces:每个块的20个字节的SHA1 Hash的?二进制格?
多文件结构如下:
files:一个字典结?br /> length:文g长度Q单位字?整数)
md5sum(可?:同单文gl构中相?br /> path:文g的\径和名字Q是一个列表结构,如\test\test.txt 列表为l4:test8test.txte
name:最上层的目录名?字符?
piece length:同单文gl构中相?br /> pieces:同单文gl构中相同?br />实例Q?br />用记事本打开一?torrent可以看来cM如下内容
d8:announce35:http://www.manfen.net:7802/announce13:creation datei1076675108e4:infod6:lengthi17799e4:name62:MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent12:piece lengthi32768e6:pieces20:?W ?w?R排T酆ee
很容易看?br />announceQ?a >http://www.manfen.net:7802/announce
creation dateQ?076675108U?02/13/04 20:25:08)
文g?MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent
文g大小Q?7799字节
文g块大=32768字节
struct a
{
void (*func)(char *);
};
void hello(char *name)
{
printf ("hello %s\n",name);
}
int main()
{
struct a a1;
a1.func = hello;
a1.func("illusion");
system("PAUSE");
return 0;
}
其实q套l合很实用了 基本上到了不用鼠标的source insight境界了,最重要的是可以在text模式下运?
使用的^台是Fedora 8
Vim和Ctags在F8安装完后pȝ已经具备
Taglist需要自׃?
1.下蝲一个Taglist的zip文gQ然后解压羃Q将taglist.vim复制到~/.vim/plugin目录下?
2.修改~/.vim/plugin/taglist.vim
?if !exists('loaded_taglist')上面加入
let Tlist_Ctags_Cmd="/usr/bin/ctags"
l果为:
|
此时Ctags和Taglist已经l合h?
3.在相应的源码目录q行ctags -R产生相应的tags文g
4.tags文g在vimq行时导入。可以修改~/.vimrcQ以后每ơ启动vim自动导入此tags文g
:set tags=/root/develop/honeyids/tags
q设|语法高?
syntax enable
syntax on
5.q行vimQ?ȀzTaglist时用:TaglistToggle命o。在左边的tags区域和正常编辑区域切换时用ctrl+2个w?
6.使用ctagsӞ ctrl+]可查看函数的定义?ctrl+oq回源文件?
要运行程序,需要在当前目录下徏立一个share文gQshare是一个空文gQ没有Q何意义,只是函数ftok需要一个文件名作参敎ͼftok另一个参数可以ؓM数字?/p>
E序q行后,分ؓ父子q程Q子q程甌׃n内存Q然后等待父q程l箋执行Q父q程首先{待子进E申请到׃n内存标识Q然后输出共享内存中的内容,Z演示׃n内存可以随时更新Q程序中在子q程里生随机数写入׃n内存供父q程d?br />代码如下Q?/p>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<signal.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
int shmID;
char * buf;
void finalize(int signo)
{
shmctl(shmID,IPC_RMID,NULL);
exit(0);
}
int main()
{
int i = 1;
key_t shmKey;
signal(SIGINT,finalize);
signal(SIGTERM,finalize);
if(fork() == 0) //子进E?br /> {
shmKey = ftok("share",16); //可以使用M大于0的数字,如果名字和数相同Q则产生的key相同?br /> if(shmKey == -1)
{
printf("创徏key出错\n");
exit(-1);
}
shmID = shmget(shmKey,20,IPC_CREAT | IPC_EXCL | 0666);
if(shmID == -1)
{
printf("创徏׃n标识出错\n");
exit(-1);
}
sleep(2); //{待父进E执行,好显C第一行ؓI?br /> while(1)
{
buf = (char *)shmat(shmID,NULL,0);
srandom(time(NULL));
sprintf(buf,"%d",random());
shmdt(buf);
}
}
else //父进E?br /> {
sleep(1); //让子q程先执行,以徏立内存映?br />
shmKey = ftok("share",16); //可以使用M大于0的数字,如果名字和数相同Q则产生的key相同?br /> if(shmKey == -1)
{
printf("创徏key出错\n");
exit(-1);
}
shmID = shmget(shmKey,20,0); //0表示如果shmKey映射的不存在则报错?br /> if(shmID == -1)
{
printf("引用׃n标识出错\n");
exit(-1);
}
while(1)
{
buf = (char *)shmat(shmID,NULL,0);
printf("%d. 现在׃n内存中的内容为:%s\n",i++,buf);
shmdt(buf);
sleep(1);
}
}
return 0;
}
下面是msgLucy.c,是徏立消息队列的
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#define PROJID 0xFF
#define LUCY 1
#define PETER 2
int mqid;
void terminate_handler(int signo)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}
int main()
{
char filenm[] = "msg";
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;
mqkey = ftok(filenm,PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}
mqid = msgget(mqkey,IPC_CREAT | IPC_EXCL | 0666);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}
signal(SIGINT, terminate_handler);
signal(SIGTERM, terminate_handler);
while(1)
{
printf("Lucy: ");
fgets(msg.mtext, 256, stdin);
if (strncmp("quit", msg.mtext, 4) == 0)
{
msgctl(mqid,IPC_RMID,NULL);
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = LUCY;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
msgrcv(mqid,&msg,256,PETER,0);
printf("Peter: %s\n", msg.mtext);
}
}
下面的是msgPeter,是和Lucy通信?E序先运行Lucy,再运行Peter
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<string.h>
#include<stdlib.h>
#define PROJID 0xFF
#define LUCY 1
#define PETER 2
int main()
{
char filenm[] = "msg";
int mqid;
key_t mqkey;
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
}msg;
int ret;
mqkey = ftok(filenm, PROJID);
if(mqkey == -1)
{
perror("ftok error: ");
exit(-1);
}
mqid = msgget(mqkey, 0);
if(mqid == -1)
{
perror("msgget error: ");
exit(-1);
}
while(1)
{
msgrcv(mqid,&msg,256,LUCY,0);
printf("Lucy: %s\n",msg.mtext);
printf("Peter: ");
fgets(msg.mtext,256,stdin);
if(strncmp("quit", msg.mtext, 4) == 0)
{
exit(0);
}
msg.mtext[strlen(msg.mtext)-1] = '\0';
msg.mtype = PETER;
msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
}
}
killQ传送信L指定的进E)
相关函数 raiseQsignal
表头文g #include<sys/types.h>
#include<signal.h>
定义函数 int kill(pid_t pid,int sig);
函数说明 kill()可以用来送参数sig指定的信L参数pid指定的进E。参数pid有几U情?
pid>0 信号传l进E识别码为pid 的进E?br />pid=0 信号传l和目前q程相同q程l的所有进E?br />pid=-1 信号广播传送给pȝ内所有的q程
pid<0 信号传l进E组识别码ؓpidl对值的所有进E?br />参数sig代表的信L号可参考附录D
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EINVAL 参数sig 不合?br />ESRCH 参数pid 所指定的进E或q程l不存在
EPERM 权限不够无法传送信L指定q程
范例 #include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
main()
{
pid_t pid;
int status;
if(!(pid= fork())){
printf(“Hi I am child process!\n?;
sleep(10);
return;
}
else{
printf(“send signal to child process (%d) \n?pid);
sleep(1);
kill(pid ,SIGABRT);
wait(&status);
if(WIFSIGNALED(status))
printf(“chile process receive signal %d\n?WTERMSIG(status));
}
}
执行 sen signal to child process(3170)
Hi I am child process!
child process receive signal 6
pauseQ让q程暂停直到信号出现Q ?br />相关函数 killQsignalQsleep
表头文g #include<unistd.h>
定义函数 int pause(void);
函数说明 pause()会o目前的进E暂停(q入睡眠状态)Q直到被信号(signal)所中断?br />
q回值 ?只返?1?br />
错误代码 EINTR 有信号到达中断了此函数?br />
sigactionQ查询或讄信号处理方式Q ?br />相关函数 signalQsigprocmaskQsigpendingQsigsuspend
表头文g #include<signal.h>
定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
函数说明 sigaction()会依参数signum指定的信Lh讄该信L处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信受?br />如参数结构sigaction定义如下
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
sa_handler此参数和signal()的参数handler相同Q代表新的信号处理函敎ͼ其他意义请参考signal()?br />sa_mask 用来讄在处理该信号时暂时将sa_mask 指定的信h|?br />sa_restorer 此参数没有用?br />sa_flags 用来讄信号处理的其他相x作,下列的数值可用?br />OR q算Q|Q组?br />A_NOCLDSTOP : 如果参数signum为SIGCHLDQ则当子q程暂停时ƈ不会通知父进E?br />SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前Q将此信号处理方式改为系l预讄方式?br />SA_RESTART:被信号中断的pȝ调用会自行重?br />SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再ơ到来?br />如果参数oldact不是NULL指针Q则原来的信号处理方式会由此l构sigaction q回?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EINVAL 参数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号
EFAULT 参数actQoldact指针地址无法存取?br />EINTR 此调用被中断
范例 #include<unistd.h>
#include<signal.h>
void show_handler(struct sigaction * act)
{
switch (act->sa_flags)
{
case SIG_DFL:printf(“Default action\n?;break;
case SIG_IGN:printf(“Ignore the signal\n?;break;
default: printf(?x%x\n?act->sa_handler);
}
}
main()
{
int i;
struct sigaction act,oldact;
act.sa_handler = show_handler;
act.sa_flags = SA_ONESHOT|SA_NOMASK;
sigaction(SIGUSR1,&act,&oldact);
for(i=5;i<15;i++)
{
printf(“sa_handler of signal %2d =?i);
sigaction(i,NULL,&oldact);
}
}
执行 sa_handler of signal 5 = Default action
sa_handler of signal 6= Default action
sa_handler of signal 7 = Default action
sa_handler of signal 8 = Default action
sa_handler of signal 9 = Default action
sa_handler of signal 10 = 0x8048400
sa_handler of signal 11 = Default action
sa_handler of signal 12 = Default action
sa_handler of signal 13 = Default action
sa_handler of signal 14 = Default action
sigaddsetQ增加一个信可信号集)
相关函数 sigemptysetQsigfillsetQsigdelsetQsigismember
表头文g #include<signal.h>
定义函数 int sigaddset(sigset_t *set,int signum);
函数说明 sigaddset()用来参数signum 代表的信号加入至参数set 信号集里?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EFAULT 参数set指针地址无法存取
EINVAL 参数signum非合法的信号~号
sigdelsetQ从信号集里删除一个信P
相关函数 sigemptysetQsigfillsetQsigaddsetQsigismember
表头文g #include<signal.h>
定义函数 int sigdelset(sigset_t * set,int signum);
函数说明 sigdelset()用来参数signum代表的信号从参数set信号集里删除?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EFAULT 参数set指针地址无法存取
EINVAL 参数signum非合法的信号~号
sigemptysetQ初始化信号集)
相关函数 sigaddsetQsigfillsetQsigdelsetQsigismember
表头文g #include<signal.h>
定义函数 int sigemptyset(sigset_t *set);
函数说明 sigemptyset()用来参数set信号集初始化q清I?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EFAULT 参数set指针地址无法存取
sigfillsetQ将所有信号加入至信号集)
相关函数 sigemptyQsigaddsetQsigdelsetQsigismember
表头文g #include<signal.h>
定义函数 int sigfillset(sigset_t * set);
函数说明 sigfillset()用来参数set信号集初始化Q然后把所有的信号加入到此信号集里?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
附加说明 EFAULT 参数set指针地址无法存取
sigismemberQ测试某个信h否已加入至信号集里)
相关函数 sigemptysetQsigfillsetQsigaddsetQsigdelset
表头文g #include<signal.h>
定义函数 int sigismember(const sigset_t *set,int signum);
函数说明 sigismember()用来试参数signum 代表的信h否已加入臛_数set信号集里。如果信号集里已有该信号则返?Q否则返??br />
q回值 ?信号集已有该信号则返?Q没有则q回0。如果有错误则返?1?br />
错误代码 EFAULT 参数set指针地址无法存取
EINVAL 参数signum 非合法的信号~号
signalQ设|信号处理方式)
相关函数 sigactionQkillQraise
表头文g #include<signal.h>
定义函数 void (*signal(int signum,void(* handler)(int)))(int);
函数说明 signal()会依参数signum 指定的信Lh讄该信L处理函数。当指定的信号到达时׃跌{到参数handler指定的函数执行。如果参数handler不是函数指针Q则必须是下列两个常C一:
SIG_IGN 忽略参数signum指定的信受?br />SIG_DFL 参数signum 指定的信号重设ؓ核心预设的信号处理方式?br />关于信号的编号和说明Q请参考附录D
q回值 ?q回先前的信号处理函数指针,如果有错误则q回SIG_ERR(-1)?br />
附加说明 在信号发生蟩转到自定的handler处理函数执行后,pȝ会自动将此处理函数换回原来系l预讄处理方式Q如果要改变此操作请改用sigaction()?br />
范例 参考alarm()或raise()?br />
sigpendingQ查询被搁置的信P
相关函数 signalQsigactionQsigprocmaskQsigsuspend
表头文g #include<signal.h>
定义函数 int sigpending(sigset_t *set);
函数说明 sigpending()会将被搁|的信号集合由参数set指针q回?br />
q回值执 行成功则q回0Q如果有错误则返?1?br />
错误代码 EFAULT 参数set指针地址无法存取
EINTR 此调用被中断?br />
sigprocmaskQ查询或讄信号遮罩Q ?br />相关函数 signalQsigactionQsigpendingQsigsuspend
表头文g #include<signal.h>
定义函数 int sigprocmask(int how,const sigset_t *set,sigset_t * oldset);
函数说明 sigprocmask()可以用来改变目前的信号遮|,其操作依参数how来决?br />SIG_BLOCK 新的信号遮罩q前的信号遮罩和参数set 指定的信号遮|作联集
SIG_UNBLOCK 目前的信号遮罩删除掉参数set指定的信号遮|?br />SIG_SETMASK 目前的信号遮罩设成参数set指定的信号遮|?br />如果参数oldset不是NULL指针Q那么目前的信号遮罩会由此指针返回?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码 EFAULT 参数setQoldset指针地址无法存取?br />EINTR 此调用被中断
sleepQ让q程暂停执行一D|_
相关函数 signalQalarm
表头文g #include<unistd.h>
定义函数 unsigned int sleep(unsigned int seconds);
函数说明 sleep()会o目前的进E暂停,直到辑ֈ参数seconds 所指定的时_或是被信h中断?br />
q回值 ?若进E暂停到参数seconds 所指定的时间则q回0Q若有信号中断则q回剩余U数?br />
ferrorQ检查文件流是否有错误发生)
相关函数 clearerrQperror
表头文g #include<stdio.h>
定义函数 int ferror(FILE *stream);
函数说明 ferror()用来查参数stream所指定的文件流是否发生了错误情况,如有错误发生则返回非0倹{?br />
q回值 ?如果文g有错误发生则返回非0倹{?br />
perrorQ打印出错误原因信息字符Ԍ
相关函数 strerror
表头文g #include<stdio.h>
定义函数 void perror(const char *s);
函数说明 perror()用来上一个函数发生错误的原因输出到标准错?stderr)。参数s所指的字符串会先打印出Q后面再加上错误原因字符丌Ӏ此错误原因依照全局变量errno的值来军_要输出的字符丌Ӏ?br />
q回值 ?br />
范例 #include<stdio.h>
main()
{
FILE *fp;
fp = fopen(?tmp/noexist?”r+?;
if(fp = =NULL) perror(“fopen?;
}
执行 $ ./perror
fopen : No such file or diretory
strerrorQ返回错误原因的描述字符Ԍ
相关函数 perror
表头文g #include<string.h>
定义函数 char * strerror(int errnum);
函数说明 strerror()用来依参数errnum的错误代码来查询光误原因的描述字符Ԍ然后该字符串指针返回?br />
q回值 ?q回描述错误原因的字W串指针?br />
范例 /* 昄错误代码0 ? 的错误原因描q?/
#include<string.h>
main()
{
int i;
for(i=0;i<10;i++)
printf(?d : %s\n?i,strerror(i));
}
执行 0 : Success
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input/output error
6 : Device not configured
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor
mkfifoQ徏立具名管道)
相关函数 pipeQpopenQopenQumask
表头文g #include<sys/types.h>
#include<sys/stat.h>
定义函数 int mkfifo(const char * pathname,mode_t mode);
函数说明 mkfifo()会依参数pathname建立Ҏ的FIFO文gQ该文g必须不存在,而参数mode文g的权限(mode%~umaskQ,因此umaskg会媄响到FIFO文g的权限。Mkfifo()建立的FIFO文g其他q程都可以用d一般文件的方式存取。当使用open()来打开FIFO文gӞO_NONBLOCK旗标会有影响
1、当使用O_NONBLOCK 旗标Ӟ打开FIFO 文g来读取的操作会立刻返回,但是若还没有其他q程打开FIFO 文g来读取,则写入的操作会返回ENXIO 错误代码?br />2、没有用O_NONBLOCK 旗标Ӟ打开FIFO 来读取的操作会等到其他进E打开FIFO文g来写入才正常q回。同样地Q打开FIFO文g来写入的操作会等到其他进E打开FIFO 文g来读取后才正常返回?br />
q回值 ?若成功则q回0Q否则返?1Q错误原因存于errno中?br />
错误代码 EACCESS 参数pathname所指定的目录\径无可执行的权限
EEXIST 参数pathname所指定的文件已存在?br />ENAMETOOLONG 参数pathname的\径名U太ѝ?br />ENOENT 参数pathname包含的目录不存在
ENOSPC 文gpȝ的剩余空间不?br />ENOTDIR 参数pathname路径中的目录存在但却非真正的目录?br />EROFS 参数pathname指定的文件存在于只读文gpȝ内?br />
范例 #include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
main()
{
char buffer[80];
int fd;
unlink(FIFO);
mkfifo(FIFO,0666);
if(fork()>0){
char s[ ] = “hello!\n?
fd = open (FIFO,O_WRONLY);
write(fd,s,sizeof(s));
close(fd);
}
else{
fd= open(FIFO,O_RDONLY);
read(fd,buffer,80);
printf(?s?buffer);
close(fd);
}
}
执行 hello!
pcloseQ关闭管道I/OQ ?br />相关函数 popen
表头文g #include<stdio.h>
定义函数 int pclose(FILE * stream);
函数说明 pclose()用来关闭由popen所建立的管道及文g指针。参数stream为先前由popen()所q回的文件指针?br />
q回值 ?q回子进E的l束状态。如果有错误则返?1Q错误原因存于errno中?br />
错误代码 ECHILD pclose()无法取得子进E的l束状态?br />
范例 参考popen()?br />
pipeQ徏立管道)
相关函数 mkfifoQpopenQreadQwriteQfork
表头文g #include<unistd.h>
定义函数 int pipe(int filedes[2]);
函数说明 pipe()会徏立管道,q将文g描述词由参数filedes数组q回。filedes[0]为管道里的读取端Qfiledes[1]则ؓ道的写入端?br />
q回值 ?若成功则q回Ӟ否则q回-1Q错误原因存于errno中?br />
错误代码 EMFILE q程已用完文件描q词最大量?br />ENFILE pȝ已无文g描述词可用?br />EFAULT 参数filedes数组地址不合法?br />
范例 /* 父进E借管道将字符东yhello!\n”传l子q程q显C?/
#include <unistd.h>
main()
{
int filedes[2];
char buffer[80];
pipe(filedes);
if(fork()>0){
/* 父进E?/
char s[ ] = “hello!\n?
write(filedes[1],s,sizeof(s));
}
else{
/*子进E?/
read(filedes[0],buffer,80);
printf(?s?buffer);
}
}
执行 hello!
popenQ徏立管道I/OQ ?br />相关函数 pipeQmkfifoQpcloseQforkQsystemQfopen
表头文g #include<stdio.h>
定义函数 FILE * popen( const char * command,const char * type);
函数说明 popen()会调用fork()产生子进E,然后从子q程中调?bin/sh -c来执行参数command的指令。参数type可用“r”代表读取,“w”代表写入。依照此type|popen()会徏立管道连到子q程的标准输备或标准输入讑֤Q然后返回一个文件指针。随后进E便可利用此文g指针来读取子q程的输备或是写入到子进E的标准输入讑֤中。此外,所有用文件指?FILE*)操作的函C都可以用,除了fclose()以外?br />
q回值 ?若成功则q回文g指针Q否则返回NULLQ错误原因存于errno中?br />
错误代码 EINVAL参数type不合法?br />
注意事项 在编写具SUID/SGID权限的程序时请尽量避免用popen()Qpopen()会承环境变量,通过环境变量可能会造成pȝ安全的问题?br />
范例 #include<stdio.h>
main()
{
FILE * fp;
char buffer[80];
fp=popen(“cat /etc/passwd?”r?;
fgets(buffer,sizeof(buffer),fp);
printf(?s?buffer);
pclose(fp);
}
AC_PREREQ(2.61)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/test.cpp])
AC_CONFIG_HEADER([config.h])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
src/Makefile
src/dohello/Makefile])
AC_OUTPUT
2.~辑configure.in
configure.scan改名成configure.inQ然后修改如下:
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(hello,1.0) #软g包名U和版本?另一U写法是写在AM_INIT_AUTOMAKE?
AC_CONFIG_SRCDIR([src/test.cpp])
#AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_RANLIB #使用了静态库~译,需要此宏定?/font>
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
#AC_CONFIG_FILES([src/Makefile src/dohello/Makefile])
AC_OUTPUT(Makefile src/Makefile src/dohello/Makefile) #需要生成的MakefileQ本例需要生成三个?br />
3.q行aclocal和autoconf
configure.in修改完后则先后执行aclocal和autoconf命o?br />
4.建立各个目录的Makefile.am文g
在顶U目录、src目录和dohello目录下分别徏立三个Makefile.am文g。内容分别如下:
目录Makefile.amQ?br />#----------------开?-----------------------------------------
AUTOMAKE_OPTIONS=foreign
SUBDIRS=src #本目录的直接下目录src需要编?br />EXTRA_DIST=doc/userguide #doc/userguide不需要编译,但要发布该文件。如果有多个文gQ则用空格分开?br />#----------------l束------------------------------------------
src目录下的Makefile.amQ?br />#----------------开?-----------------------------------------
AUTOMAKE_OPTIONS=foreign
SUBDIRS=dohello #本目录的直接下目录dohello需要编?br />bin_PROGRAMS=hello #本目录的文g~译成可执行文ghello。如有多个,用空格分开。然后在下面分别写它们的SOURCE和LDADD?br />hello_SOURCES=test.cpp #~译hello需要的源文件列表,如有多个Q用I格分开?br />hello_LDADD=dohello/libhello.a #~译hello需要的库文件列表。如有多个,用空格分开?br />#----------------l束------------------------------------------
dohello目录下的Makefile.am
#----------------开?-----------------------------------------
AUTOMAKE_OPTIONS=foreign
noinst_LIBRARIES=libhello.a #本目录下的代码编译成libhello.a库。不需要发布。如果需要发布,则写成bin_LIBRARIES。注意,库的名称格式必需?libxxx.a。因为编译静态库Qconfigure.in需要定义AC_PROG_RANLIB宏?br />libhello_a_SOURCES=dohello.h dohello.cpp #~译libhello.a需要的源文件。注意将库名UC?.'h?_'受?
#----------------l束------------------------------------------
5.q行automake
以上几个Makefile.am都书写完毕后Q运行automake --add-missing?br />
6.q行./configure和make
上面步骤完成后,先后q行./configure和make完成~译。如果编译成功,q行make dist可以所有文件打包成hello-1.0.tar.gz?/p>
一、Makefile介绍
Makefile是用于自动编译和链接的,一个工E有很多文gl成Q每一个文件的改变都会D工程的重新链接,但是不是所有的文g都需要重新编译,Makefile中纪录有文g的信息,在make时会军_在链接的时候需要重新编译哪些文件?/p>
Makefile的宗旨就是:让编译器知道要编译一个文仉要依赖其他的哪些文g。当那些依赖文g有了改变Q编译器会自动的发现最l的生成文g已经q时Q而重新编译相应的模块?/p>
Makefile的基本结构不是很复杂Q但当一个程序开发h员开始写MakefileӞl常会怀疑自己写的是否符合惯例,而且自己写的 Makefilel常和自q开发环境相兌Q当pȝ环境变量或\径发生了变化后,Makefile可能q要跟着修改。这样就造成了手工书?Makefile的诸多问题,automake恰好能很好地帮助我们解决q些问题?/p>
使用automakeQ程序开发h员只需要写一些简单的含有预定义宏的文Ӟ由autoconfҎ一个宏文g生成configureQ由 automakeҎ另一个宏文g生成Makefile.inQ再使用configure依据Makefile.in来生成一个符合惯例的 Makefile。下面我们将详细介绍Makefile的automake生成Ҏ?/p>
二、用的环境
本文所提到的程序是ZLinux发行版本QFedora Core release 1Q它包含了我们要用到的autoconfQautomake?/p>
三、从helloworld入手
我们从大家最怋用的例子E序helloworld开始?/p>
下面的过E如果简单地说来是Q?/p>
新徏三个文gQ?/p>
helloworld.c
configure.in
Makefile.am
然后执行Q?br /> aclocal
autoconf
automake --add-missing
./configure
make
./helloworld
可以看到Makefile被生出来,而且可以helloworld.c~译通过?/p>
很简单吧Q几条命令就可以做出一个符合惯例的MakefileQ感觉如何呀?/p>
现在开始介l详l的q程Q?/p>
1、徏目录
在你的工作目录下Z个helloworld目录Q我们用它来存放helloworldE序及相xӞ如在/home/my/build下:
$ mkdir helloword
$ cd helloworld
2?helloworld.c
然后用你自己最喜欢的编辑器写一个hellowrold.c文gQ如命oQvi helloworld.c。用下面的代码作ؓhelloworld.c的内宏V?/p>
int main(int argc, char** argv)
{
printf("Hello, Linux World!\n");
return 0;
}
完成后保存退出?/p>
现在在helloworld目录下就应该有一个你自己写的helloworld.c了?/p>
3、生成configure
我们使用autoscan命o来帮助我们根据目录下的源代码生成一个configure.in的模板文件?/p>
命oQ?/p>
$ autoscan
$ ls
configure.scan helloworld.c
执行后在hellowrold目录下会生成一个文Ӟconfigure.scanQ我们可以拿它作为configure.in的蓝本?/p>
现在configure.scan改名为configure.inQƈ且编辑它Q按下面的内容修改,L无关的语句:
============================configure.in内容开?========================================
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT(Makefile)
============================configure.in内容l束=========================================
然后执行命oaclocal和autoconfQ分别会产生aclocal.m4及configure两个文gQ?/p>
$ aclocal
$ls
aclocal.m4 configure.in helloworld.c
$ autoconf
$ ls
aclocal.m4 autom4te.cache configure configure.in helloworld.c
大家可以看到configure.in内容是一些宏定义Q这些宏lautoconf处理后会变成查系l特性、环境变量、Y件必ȝ参数的shell脚本?/p>
autoconf 是用来生成自动配|Y件源代码脚本QconfigureQ的工具。configure脚本能独立于autoconfq行Q且在运行的q程中,不需要用Lq预?/p>
要生成configure文gQ你必须告诉autoconf如何扑ֈ你所用的宏。方式是使用aclocalE序来生成你的aclocal.m4?/p>
aclocalҎconfigure.in文g的内容,自动生成aclocal.m4文g。aclocal是一个perl 脚本E序Q它的定义是Q“aclocal - create aclocal.m4 by scanning configure.ac”?/p>
autoconf从configure.inq个列D~译软g时所需要各U参数的模板文g中创建configure?/p>
autoconf需要GNU m4宏处理器来处理aclocal.m4Q生成configure脚本?/p>
m4是一个宏处理器。将输入拯到输出,同时宏展开。宏可以是内嵌的Q也可以是用户定义的。除了可以展开宏,m4q有一些内建的函数Q用来引用文Ӟ执行命oQ整数运,文本操作Q@环等。m4既可以作为编译器的前端,也可以单独作Z个宏处理器?br />
4、新建Makefile.am
新徏Makefile.am文gQ命令:
$ vi Makefile.am
内容如下:
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c
automake会根据你写的Makefile.am来自动生成Makefile.in?/p>
Makefile.am中定义的宏和目标,会指导automake生成指定的代码。例如,宏bin_PROGRAMS导致编译和q接的目标被生成?/p>
5、运行automake
命oQ?br />$ automake --add-missing
configure.in: installing `./install-sh'
configure.in: installing `./mkinstalldirs'
configure.in: installing `./missing'
Makefile.am: installing `./depcomp'
automake会根据Makefile.am文g产生一些文Ӟ包含最重要的Makefile.in?/p>
6、执行configure生成Makefile
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ANSI C... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
$ ls -l Makefile
-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile
你可以看刎ͼ此时Makefile已经产生出来了?/p>
7、用Makefile~译代码
$ make
if gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -
DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="helloworld" -DVERSION="1.0"
-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" \
-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; \
then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; \
else rm -f ".deps/helloworld.Tpo"; exit 1; \
fi
gcc -g -O2 -o helloworld helloworld.o
q行helloworld
$ ./helloworld
Hello, Linux World!
q样helloworldq译出来了Q你如果按上面的步骤来做的话Q应该也会很Ҏ地编译出正确的helloworld文g。你q可以试着使用一?其他的make命oQ如make cleanQmake installQmake distQ看看它们会l你什么样的效果。感觉如何?自己也能写出q么专业的MakefileQ老板一定会对你刮目相看?/p>
四、深入浅?/p>
针对上面提到的各个命令,我们再做些详l的介绍?/p>
1?autoscan
autoscan是用来扫描源代码目录生成configure.scan文g的。autoscan可以用目录名做ؓ参数Q但如果你不使用参数?话,那么autoscan认Z用的是当前目录。autoscan扫描你所指定目录中的源文Ӟq创建configure.scan文g?/p>
2?configure.scan
configure.scan包含了系l配|的基本选项Q里面都是一些宏定义。我们需要将它改名ؓconfigure.in
3?aclocal
aclocal是一个perl 脚本E序。aclocalҎconfigure.in文g的内容,自动生成aclocal.m4文g。aclocal的定义是Q“aclocal - create aclocal.m4 by scanning configure.ac”?/p>
4?autoconf
autoconf是用来生configure文g的。configure是一个脚本,它能讄源程序来适应各种不同的操作系l^収ͼq且Ҏ不同的系l来产生合适的MakefileQ从而可以你的源代码能在不同的操作pȝq_上被~译出来?/p>
configure.in文g的内Ҏ一些宏Q这些宏l过autoconf 处理后会变成查系l特性、环境变量、Y件必ȝ参数的shell脚本。configure.in文g中的宏的序q没有规定,但是你必d所有宏的最?面和最后面分别加上AC_INIT宏和AC_OUTPUT宏?/p>
在configure.in中:
#可C注释,q个宏后面的内容被忽略?/p>
AC_INIT(FILE)
q个宏用来检查源代码所在的路径?/p>
AM_INIT_AUTOMAKE(PACKAGE, VERSION)
q个宏是必须的,它描qC我们要生成的Y件包的名字及其版本号QPACKAGE是Y件包的名字,VERSION是版本号。当你用make dist命oӞ它会l你生成一个类似helloworld-1.0.tar.gz的Y件发行包Q其中就有对应的软g包的名字和版本号?/p>
AC_PROG_CC
q个宏将查系l所用的C~译器?/p>
AC_OUTPUT(FILE)
q个宏是我们要输出的Makefile的名字?/p>
我们在用automakeӞ实际上还需要用到其他的一些宏Q但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文g?/p>
产生了configure.in和aclocal.m4 两个宏文件后Q我们就可以使用autoconf来生configure文g了?/p>
5?Makefile.am
Makefile.am是用来生成Makefile.in的,需要你手工书写。Makefile.am中定义了一些内容:
AUTOMAKE_OPTIONS
q个是automake的选项。在执行automakeӞ它会查目录下是否存在标准GNU软g包中应具备的各种文gQ例如AUTHORS、ChangeLog、NEWS{文件。我们将其设|成foreignӞautomake会改用一般Y件包的标准来查?/p>
bin_PROGRAMS
q个是指定我们所要生的可执行文件的文g名。如果你要生多个可执行文gQ那么在各个名字间用I格隔开?/p>
helloworld_SOURCES
q个是指定生“helloworld”时所需要的源代码。如果它用到了多个源文gQ那么请使用I格W号它们隔开。比如需?helloworld.hQhelloworld.c那么请写成helloworld_SOURCES= helloworld.h helloworld.c?/p>
如果你在bin_PROGRAMS定义了多个可执行文gQ则对应每个可执行文仉要定义相对的filename_SOURCES?/p>
LIBS
q个用来指定链接的程序库。如LIBS += -lpthreadQ指定链接pthread库?/p>
6?automake
我们使用automake --add-missing来生Makefile.in?/p>
选项--add-missing的定义是“add missing standard files to package”,它会让automake加入一个标准的软g包所必须的一些文件?/p>
我们用automake产生出来的Makefile.in文g是符合GNU Makefile惯例的,接下来我们只要执行configureq个shell 脚本可以生合适的 Makefile 文g了?/p>
7?Makefile
在符合GNU Makefiel惯例的Makefile中,包含了一些基本的预先定义的操作:
make
ҎMakefile~译源代码,q接Q生成目标文Ӟ可执行文件?br />make clean
清除上次的make命o所产生的object文gQ后~为?o”的文gQ及可执行文件?br />make install
编译成功的可执行文件安装到pȝ目录中,一般ؓ/usr/local/bin目录?br />make dist
产生发布软g包文Ӟ即distribution packageQ。这个命令将会将可执行文件及相关文g打包成一个tar.gz压羃的文件用来作为发布Y件的软g包?br />它会在当前目录下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文g。PACKAGE和VERSIONQ是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)?/p>
make distcheck
生成发布软g包ƈ对其q行试查,以确定发布包的正性。这个操作将自动把压~包文g解开Q然后执行configure命oQƈ且执行makeQ来认~译不出现错误,最后提CZ软g包已l准备好Q可以发布了?/p>
===============================================
helloworld-1.0.tar.gz is ready for distribution
===============================================
make distclean
cMmake cleanQ但同时也将configure生成的文件全部删除掉Q包括Makefile?/p>
五、结束语
通过上面的介l,你应该可以很Ҏ地生成一个你自己的符合GNU惯例的Makefile文g及对应的目文g?/p>
如果你想写出更复杂的且符合惯例的MakefileQ你可以参考一些开放代码的目中的configure.in和Makefile.am文gQ比如:嵌入式数据库sqliteQ单元测试cppunit?/p>
D 无法中断的休眠状态(通常 IO 的进E)Q?/p> R 正在q行可中在队列中可过行的Q?/p> S 处于休眠状态; T 停止或被q踪Q?/p> W q入内存交换Q从内核2.6开始无效)Q?/p> X L的进E(从来没见q)Q?/p> Z 僵尸q程Q?/p>
< 优先U高的进E?/p> N 优先U较低的q程 L 有些被锁进内存Q?/p> s q程的领D(在它之下有子q程Q; l 多进E的Q?CLONE_THREAD, cM NPTL pthreadsQ; + 位于后台的进E组Q?/p> |
理解 Proc 文gpȝ |
---|
摘要: Linux 内核提供了一U通过 /proc 文gpȝQ在q行时访问内核内部数据结构、改变内核设|的机制。尽在各种gq_上的 Linux pȝ?/proc 文gpȝ的基本概念都是相同的Q但本文只讨论基?intel x86 架构?Linux /proc 文gpȝ? |
/proc 文gpȝ是一U内核和内核模块用来向进E?(process) 发送信息的机制 (所以叫?/proc)。这个伪文gpȝ让你可以和内核内部数据结构进行交互,获取 有关q程的有用信息,在运行中 (on the fly) 改变讄 (通过改变内核参数)?与其他文件系l不同,/proc 存在于内存之中而不是硬盘上。如果你察看文g /proc/mounts (?mount 命o一样列出所有已l加载的文gpȝ)Q你会看到其?一行是q样的:
grep proc /proc/mounts /proc /proc proc rw 0 0
/proc 由内核控Ӟ没有承蝲 /proc 的设备。因?/proc 主要存放由内核控制的状态信息,所以大部分q些信息的逻辑位置位于内核控制的内存。对 /proc q行一?'ls -l' 可以看到大部分文仉?0 字节大的Q不q察看这些文件的时候,实可以看到一些信息。这怎么可能Q这是因?/proc 文gpȝ和其他常规的文gpȝ一h自己注册到虚拟文件系l层 (VFS) 了。然而,直到?VFS 调用它,h文g、目录的 i-node 的时候,/proc 文gpȝ才根据内怸的信息徏立相应的文g和目录?
如果pȝ中还没有加蝲 proc 文gpȝQ可以通过如下命o加蝲 proc 文gpȝQ?
mount -t proc proc /proc
上述命o成功加载你?proc 文gpȝ。更多细节请阅读 mount 命o?man page?/proc 的文件可以用于访问有兛_核的状态、计机的属性、正在运行的q程的状态等信息。大部分 /proc 中的文g和目录提供系l物理环境最新的信息。尽?/proc 中的文g是虚拟的Q但它们仍可以用Q何文件编辑器或像'more', 'less'?'cat'q样的程序来查看。当~辑E序试图打开一个虚拟文件时Q这个文件就通过内核中的信息被凭I地 (on the fly) 创徏了。这是一些我从我的系l中得到的一些有结果:
$ ls -l /proc/cpuinfo -r--r--r-- 1 root root 0 Dec 25 11:01 /proc/cpuinfo $ file /proc/cpuinfo /proc/cpuinfo: empty $ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 8 model name : Pentium III (Coppermine) stepping : 6 cpu MHz : 1000.119 cache size : 256 KB fdiv_bug : no hlt_bug : no sep_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 2 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr xmm bogomips : 1998.85 processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 8 model name : Pentium III (Coppermine) stepping : 6 cpu MHz : 1000.119 cache size : 256 KB fdiv_bug : no hlt_bug : no sep_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 2 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr xmm bogomips : 1992.29
proc 文gpȝ可以被用于收集有用的关于pȝ和运行中的内核的信息。下面是一些重要的文gQ?
/proc 文gpȝ可以用于获取q行中的q程的信息。在 /proc 中有一些编L子目录。每个编L目录对应一个进E?id (PID)。这P每一个运行中的进E?/proc 中都有一个用它的 PID 命名的目录。这些子目录中包含可以提供有兌E的状态和环境的重要细节信息的文g。让我们试着查找一个运行中的进E?
$ ps -aef | grep mozilla root 32558 32425 8 22:53 pts/1 00:01:23 /usr/bin/mozilla上述命o昄有一个正在运行的 mozilla q程?PID ?32558。相对应的,/proc 中应该有一个名?32558 的目?br />
$ ls -l /proc/32558 total 0 -r--r--r-- 1 root root 0 Dec 25 22:59 cmdline -r--r--r-- 1 root root 0 Dec 25 22:59 cpu lrwxrwxrwx 1 root root 0 Dec 25 22:59 cwd -> /proc/ -r-------- 1 root root 0 Dec 25 22:59 environ lrwxrwxrwx 1 root root 0 Dec 25 22:59 exe -> /usr/bin/mozilla* dr-x------ 2 root root 0 Dec 25 22:59 fd/ -r--r--r-- 1 root root 0 Dec 25 22:59 maps -rw------- 1 root root 0 Dec 25 22:59 mem -r--r--r-- 1 root root 0 Dec 25 22:59 mounts lrwxrwxrwx 1 root root 0 Dec 25 22:59 root -> // -r--r--r-- 1 root root 0 Dec 25 22:59 stat -r--r--r-- 1 root root 0 Dec 25 22:59 statm -r--r--r-- 1 root root 0 Dec 25 22:59 status文g "cmdline" 包含启动q程时调用的命o行?envir" q程的环境变两?"status" 是进E的状态信息,包括启动q程的用L用户ID (UID) 和组ID(GID) Q父q程ID (PPID)Q还有进E当前的状态,比如"Sleelping"?Running"。每个进E的目录都有几个W号链接Q?cwd"是指向进E当前工作目录的W号链接Q?exe"指向q行的进E的可执行程序,"root"指向被这个进E看作是根目录的目录 (通常?/")。目?fd"包含指向q程使用的文件描q符的链接?"cpu"仅在q行 SMP 内核时出玎ͼ里面是按 CPU 划分的进E时间?
/proc/self 是一个有的子目录,它得程序可以方便地使用 /proc 查找本进E地信息?proc/self 是一个链接到 /proc 中访?/proc 的进E所对应?PID 的目录的W号链接?br />
上面讨论的大部分 /proc 的文件是只读的。而实际上 /proc 文gpȝ通过 /proc 中可d的文件提供了对内核的交互机制。写q些文g可以改变内核的状态,因而要慎重改动q些文g?proc/sys 目录存放所有可d的文件的目录Q可以被用于改变内核行ؓ?/p>
/proc/sys/kernel - q个目录包含反通用内核行ؓ的信息?/proc/sys/kernel/{domainname, hostname} 存放着机器/|络的域名和L名。这些文件可以用于修改这些名字?br />
$ hostname machinename.domainname.com $ cat /proc/sys/kernel/domainname domainname.com $ cat /proc/sys/kernel/hostname machinename $ echo "new-machinename" > /proc/sys/kernel/hostname $ hostname new-machinename.domainname.comq样Q通过修改 /proc 文gpȝ中的文gQ我们可以修改主机名。很多其他可配置的文件存在于 /proc/sys/kernel/。这里不可能列出所有这些文Ӟ读者可以自己去q个目录查看以得到更多细节信息?br />另一个可配置的目录是 /proc/sys/net。这个目录中的文件可以用于修Ҏ?|络的网l属性。比如,单修改一个文Ӟ你可以在|络上瘾藏匿的计机?br />
$ echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_allq将在网l上瘾藏你的机器Q因为它不响?icmp_echo。主机将不会响应其他L发出?ping 查询?br />
$ ping machinename.domainname.com no answer from machinename.domainname.com要改回缺省设|,只要
$ echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all/proc/sys 下还有许多其它可以用于改变内核属性。读者可以通过参考文?[1], [2] 获取更多信息?
/proc 文gpȝ提供了一个基于文件的 Linux 内部接口。它可以用于定pȝ的各U不同设备和q程的状态。对他们q行配置。因而,理解和应用有兌个文件系l的知识是理解你?Linux pȝ的关键?br />
守护q程的编E本wƈ不复杂,复杂的是各种版本的Unix的实现机制不相同,造成不同 Unix环境下守护进E的~程规则q不一致。需要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System VQ到Linux会出现错误的。下面将l出Linux下守护进E的~程要点和详l实例?
一Q?守护q程及其Ҏ?
守护q程最重要的特性是后台q行。在q一点上DOS下的帔R内存E序TSR与之怼。其ơ,守护q程必须与其q行前的环境隔离开来。这些环境包括未 关闭的文件描q符Q控制终端,会话和进E组Q工作目录以及文件创建掩模等。这些环境通常是守护进E从执行它的父进E(特别是shellQ中l承下来的。最 后,守护q程的启动方式有其特D之处。它可以在Linuxpȝ启动时从启动脚本/etc/rc.d中启动,可以׃业规划进Ecrond启动Q还可以q L端(通常?shellQ执行?
MQ除开q些Ҏ性以外,守护q程与普通进E基本上没有什么区别。因此,~写守护q程实际上是把一个普通进E按照上q的守护q程的特性改造成为守护进E。如果对q程有比较深入的认识更Ҏ理解和编E了?
二. 守护q程的编E要?
前面讲过Q不同Unix环境下守护进E的~程规则q不一致。所q的是守护进E的~程原则其实都一P区别在于具体的实现细节不同。这个原则就是要? _护进E的Ҏ。同ӞLinux是基于Syetem V的SVR4q循Posix标准Q实现v来与BSD4相比更方ѝ编E要点如下;
1. 在后台运行?
为避免挂h制终端将Daemon攑օ后台执行。方法是在进E中调用fork使父q程l止Q让Daemon在子q程中后台执行?
if(pid=fork())
exit(0); //是父q程Q结束父q程Q子q程l箋
2. q控制l端Q登录会话和q程l?
有必要先介绍一下Linux中的q程与控制终端,d会话和进E组之间的关p:q程属于一个进E组Q进E组PGIDQ就是进E组长的q程? QPIDQ。登录会话可以包含多个进E组。这些进E组׃n一个控制终端。这个控制终端通常是创E的dl端。控制终端,d会话和进E组通常是从父进 E承下来的。我们的目的是要摆脱它们,使之不受它们的媄响。方法是在第1点的基础上,调用setsid()使进E成Z话组长:
setsid();
说明Q当q程是会话组长时setsid()调用p|。但W一点已l保证进E不是会话组ѝsetsid()调用成功后,q程成ؓ新的会话l长和新的进E组长,q与原来的登录会话和q程l脱R由于会话过E对控制l端的独占性,q程同时与控制终端脱R?
3. 止q程重新打开控制l端
现在Q进E已l成为无l端的会话组ѝ但它可以重新申h开一个控制终端。可以通过使进E不再成Z话组长来止q程重新打开控制l端Q?
if(pid=fork()) exit(0); //l束W一子进E,W二子进El(W二子进E不再是会话l长Q?
4. 关闭打开的文件描q符
q程从创建它的父q程那里l承了打开的文件描q符。如不关闭,会费pȝ资源Q造成q程所在的文gpȝ无法怸以及引v无法预料的错误。按如下Ҏ关闭它们Q?
for(i=0;i 关闭打开的文件描q符close(i);>
5. 改变当前工作目录
q程zdӞ其工作目录所在的文gpȝ不能怸。一般需要将工作目录改变到根目录。对于需要{储核心,写运行日志的q程工作目录改变到特定目录?/tmpchdir("/")
6. 重设文g创徏掩模
q程从创建它的父q程那里l承了文件创建掩模。它可能修改守护q程所创徏的文件的存取位。ؓ防止q一点,文件创建掩模清除:umask(0);
7. 处理SIGCHLD信号
处理SIGCHLD信号q不是必ȝ。但对于某些q程Q特别是服务器进E往往在请求到来时生成子进E处理请求。如果父q程不等待子q程l束Q子q程 成为僵进E(zombieQ从而占用系l资源。如果父q程{待子进E结束,增加父q程的负担,影响服务器进E的q发性能。在Linux下可以简单地 ?SIGCHLD信号的操作设为SIG_IGN?
signal(SIGCHLD,SIG_IGN);
q样Q内核在子进E结束时不会产生僵尸q程。这一点与BSD4不同QBSD4下必L式等待子q程l束才能释放僵尸q程?
三. 守护q程实例
守护q程实例包括两部分:ȝ序test.c和初始化E序init.c。主E序每隔一分钟?tmp目录中的日志test.log报告q行状态。初始化E序中的init_daemon函数负责生成守护q程。读者可以利用init_daemon函数生成自己的守护进E?
1Q?init.c清单
Qi nclude < unistd.h >
Qi nclude < signal.h >
Qi nclude < sys/param.h >
Qi nclude < sys/types.h >
Qi nclude < sys/stat.h >
void init_daemon(void)
{
int pid;
int i;
if(pid=fork())
exit(0);//是父q程Q结束父q程
else if(pid< 0)
exit(1);//forkp|Q退?br />//是第一子进E,后台l箋执行
setsid();//W一子进E成为新的会话组长和q程l长
//q与控制l端分离
if(pid=fork())
exit(0);//是第一子进E,l束W一子进E?br />else if(pid< 0)
exit(1);//forkp|Q退?br />//是第二子q程Ql?br />//W二子进E不再是会话l长
for(i=0;i< NOFILE;++i)//关闭打开的文件描q符
close(i);
chdir("/tmp");//改变工作目录?tmp
umask(0);//重设文g创徏掩模
return;
}
2Q?test.c清单
Qi nclude < stdio.h >
Qi nclude < time.h >
void init_daemon(void);//守护q程初始化函?br />
main()
{
FILE *fp;
time_t t;
init_daemon();//初始化ؓDaemon
while(1)//每隔一分钟向test.log报告q行状?br />{
sleep(60);//睡眠一分钟
if((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"Im here at %sn",asctime(localtime(&t)) );
fclose(fp);
}
}
}
以上E序在RedHat Linux6.0下编译通过。步骤如下:
~译Qgcc -g -o test init.c test.c
执行Q?/test
查看q程Qps -ef
从输出可以发现test守护q程的各U特性满上面的要求?
在Alpha/AXPq_上引导Linux通常有两U方法,一U是由MILO及其他类似的引导E序引导Q另一U是由Firmware直接引导。MILO功能与i386q_的LILO相近Q但内置有基本的盘驱动E序Q如IDE、SCSI{)Q以及常见的文gpȝ驱动E序Q如ext2Qiso9660{)Q?firmware有ARC、SRM两种形式QARChcBIOS界面Q甚臌有多重引导的讄Q而SRM则具有功能强大的命o行界面,用户可以在控制台上用boot{命令引导系l。ARC有分区(PartitionQ的概念Q因此可以访问到分区的首扇区Q而SRM只能控制{l磁盘的首扇区。两Ufirmware都可以通过引导MILO来引导LinuxQ也可以直接引导Linux的引g码?/p>
“arch/alpha/boot”下是制作Linux Bootloader的文件。“head.S”文件提供了?OSF PAL/1的调用入口,它将被编译后|于引导扇区QARC的分区首扇区或SRM的磁?扇区Q,得到控制后初始化一些数据结构,再将控制转给“main.c”中的start_kernel()Q?start_kernel()向控制台输出一些提C,调用pal_init()初始化PAL代码Q调用openboot() 打开引导讑֤Q通过dFirmware环境Q,调用load()核心代码加载到START_ADDRQ见 “include/asm-alpha/system.h”)Q再Firmware中的核心引导参数加蝲到ZERO_PAGE(0) 中,最后调用runkernel()控制{l?x100000的kernelQbootloader部分l束?/p>
Bootloader中用的所有“srm_”函数在“arch/alpha/lib/”中定义?/p>
以上q种Boot方式是一U最单的方式Q即不需其他工具p引导KernelQ前提是按照 Makefile的指|生成bootimage文gQ内含以上提到的bootloader以及vmlinuxQ然后将 bootimage写入自磁盘引导扇区始的位|中?/p>
当采用MILOq样的引导程序来引导LinuxӞ不需要上面所说的BootloaderQ而只需?vmlinux或vmlinux.gzQ引导程序会d解压加蝲内核?x1000Q小内核Q或0x100000Q大内核Q,q直接进入内核引导部分,x文的W二节?/p>
对于I386q_
i386pȝ中一般都有BIOS做最初的引导工作Q那是四个主分区表中的第一个可引导分区的第一个扇区加载到实模式地址0x7c00上,然后控制{交给它?
在“arch/i386/boot”目录下Qbootsect.S是生成引导扇区的汇编源码Q它首先自己拷贝到0x90000上,然后紧接其后的setup部分Q第二扇区)拯?x90200Q将真正的内总码拷贝到0x100000。以上这些拷贝动作都是以bootsect.S、setup.S以及vmlinux在磁盘上q箋存放为前提的Q也是_我们的bzImage文g或者zImage文g是按照bootsectQsetupQ?vmlinuxq样的顺序组l,q存放于始于引导分区的首扇区的连l磁盘扇Z中?/blockquote>bootsect.S完成加蝲动作后,q接蟩转到0x90200Q这里正是setup.S的程序入口?setup.S的主要功能就是将pȝ参数Q包括内存、磁盘等Q由BIOSq回Q拷贝到 0x90000-0x901FF内存中,q个地方正是bootsect.S存放的地方,q时它将被系l参数覆盖。以后这些参数将׃护模式下的代码来d?/blockquote>除此之外Qsetup.Sq将video.S中的代码包含q来Q检和讄昄器和昄模式。最后,setup.S系l{换到保护模式Qƈ跌{?x100000Q对于bzImage格式的大内核?0x100000Q对于zImage格式的是0x1000Q的内核引导代码QBootloaderq程l束?/blockquote>对于2.4.x版内?/b>
没有什么变化?
![]()
![]()
![]()
![]()
回页?/font>
对于I386q_
在i386体系l构中,因ؓi386本n的问题,?arch/alpha/kernel/head.S"中需要更多的讄Q但最l也是通过call SYMBOL_NAME(start_kernel)转到start_kernel()q个体系l构无关的函CL行了?所不同的是Q在i386pȝ中,当内总bzImage的Ş式压~,卛_内核方式Q__BIG_KERNEL__Q压~时需要预先处理bootsect.S和setup.SQ按照大核模式?(CPP) 处理生成bbootsect.S和bsetup.SQ然后再~译生成相应?o文gQƈ使用 "arch/i386/boot/compressed/build.c"生成的build工具Q将实际的内核(未压~的Q含 kernel中的head.S代码Q与"arch/i386/boot/compressed"下的head.S和misc.c合成CP其中的head.S代替?arch/i386/kernel/head.S"的位|,由Bootloader引导执行Qstartup_32入口Q,然后它调用misc.c中定义的decompress_kernel()函数Q?"lib/inflate.c"中定义的gunzip()内核解压到0x100000Q再转到其上执行 "arch/i386/kernel/head.S"中的startup_32代码?/blockquote>对于2.4.x版内?/b>
没有变化?
![]()
![]()
![]()
![]()
回页?/font>
start_kernel()中调用了一pd初始化函敎ͼ以完成kernel本n的设|。这些动作有的是公共的,有的则是需要配|的才会执行的?/p>
在start_kernel()函数中,
xstart_kernel()l束Q基本的核心环境已经建立h了?
- 输出Linux版本信息Qprintk(linux_banner)Q?
- 讄与体pȝ构相关的环境Qsetup_arch()Q?
- 表l构初始化(paging_init()Q?
- 使用"arch/alpha/kernel/entry.S"中的入口点设|系l自陷入口(trap_init()Q?
- 使用alpha_mvl构和entry.S入口初始化系lIRQQinit_IRQ()Q?
- 核心q程调度器初始化Q包括初始化几个~省的Bottom-halfQsched_init()Q?
- 旉、定时器初始化(包括dCMOS旉、估主频、初始化定时器中断等Qtime_init()Q?
- 提取q分析核心启动参敎ͼ从环境变量中d参数Q设|相应标志位{待处理Q(parse_options()Q?
- 控制台初始化Qؓ输出信息而先于PCI初始化,console_init()Q?
- 剖析器数据结构初始化Qprof_buffer和prof_len变量Q?
- 核心Cache初始化(描述Cache信息的CacheQkmem_cache_init()Q?
- 延迟校准Q获得时钟jiffies与CPU主频ticks的gq,calibrate_delay()Q?
- 内存初始化(讄内存上下界和表初始|mem_init()Q?
- 创徏和设|内部及通用cacheQ?slab_cache"Qkmem_cache_sizes_init()Q?
- 创徏uid taskcount SLAB cacheQ?uid_cache"Quidcache_init()Q?
- 创徏文gcacheQ?files_cache"Qfilescache_init()Q?
- 创徏目录cacheQ?dentry_cache"Qdcache_init()Q?
- 创徏与虚存相关的cacheQ?vm_area_struct"Q?mm_struct"Qvma_init()Q?
- 块设备读写缓冲区初始化(同时创徏"buffer_head"cache用户加速访问,buffer_init()Q?
- 创徏cacheQ内存页hash表初始化Qpage_cache_init()Q?
- 创徏信号队列cacheQ?signal_queue"Qsignals_init()Q?
- 初始化内存inode表(inode_init()Q?
- 创徏内存文g描述W表Q?filp_cache"Qfile_table_init()Q?
- 查体pȝ构漏z(对于alphaQ此函数为空Qcheck_bugs()Q?
- SMP机器其余CPUQ除当前引导CPUQ初始化Q对于没有配|SMP的内核,此函CؓI,smp_init()Q?
- 启动initq程Q创建第一个核心线E,调用init()函数Q原执行序列调用cpu_idle() {待调度Qinit()Q?
对于I386q_
i386q_上的内核启动q程与此基本相同Q所不同的主要是实现方式?对于2.4.x版内?/b>
2.4.x中变化比较大Q但基本q程没变Q变动的是各个数据结构的具体实现Q比如Cache?
回页?/font> init()函数作ؓ核心U程Q首先锁定内核(仅对SMP机器有效Q,然后调用 do_basic_setup()完成外设及其驱动E序的加载和初始化。过E如下:
xdo_basic_setup()函数q回init()Q在释放启动内存D(free_initmem()Qƈl内核解锁以后,init()打开/dev/console讑֤Q重定向stdin、stdout和stderr到控制台Q最后,搜烦文gpȝ中的initE序Q或者由init=命o行参数指定的E序Q,q?execve()pȝ调用加蝲执行initE序?
- ȝ初始化(比如pci_init()Q?
- |络初始化(初始化网l数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,调用protocolsl构中包含的所有协议的初始化过E,sock_init()Q?
- 创徏bdflush核心U程Qbdflush()q程帔R核心I间Q由核心唤醒来清理被写过的内存缓冲区Q当bdflush()由kernel_thread()启动后,它将自己命名为kflushdQ?
- 创徏kupdate核心U程Qkupdate()q程帔R核心I间Q由核心按时调度执行Q将内存~冲Z的信息更新到盘中,更新的内容包括超U块和inode表)
- 讄q启动核心调늺EkswapdQؓ了防止kswapd启动时将版本信息输出到其他信息中_核心U调用kswapd_setup()讄kswapdq行所要求的环境,然后再创?kswapd核心U程Q?
- 创徏事g理核心U程Qstart_context_thread()函数启动context_thread()q程Qƈ重命名ؓkeventdQ?
- 讑֤初始化(包括q口parport_init()、字W设备chr_dev_init()、块讑֤ blk_dev_init()、SCSI讑֤scsi_dev_init()、网l设备net_dev_init()、磁盘初始化及分区检查等{,device_setup()Q?
- 执行文g格式讄Qbinfmt_setup()Q?
- 启动M使用__initcall标识的函敎ͼ方便核心开发者添加启动函敎ͼdo_initcalls()Q?
- 文gpȝ初始化(filesystem_setup()Q?
- 安装root文gpȝQmount_root()Q?
init()函数到此l束Q内核的引导部分也到此结束了Q这个由start_kernel()创徏的第一个线E已l成Z个用h式下的进E了。此时系l中存在着六个q行实体Q?/p>
- start_kernel()本n所在的执行体,q其实是一?手工"创徏的线E,它在创徏了init()U程以后p入cpu_idle()循环了,它不会在q程Q线E)列表中出?
- initU程Q由start_kernel()创徏Q当前处于用h,加蝲了initE序
- kflushd核心U程Q由initU程创徏Q在核心态运行bdflush()函数
- kupdate核心U程Q由initU程创徏Q在核心态运行kupdate()函数
- kswapd核心U程Q由initU程创徏Q在核心态运行kswapd()函数
- keventd核心U程Q由initU程创徏Q在核心态运行context_thread()函数
对于I386q_
基本相同?对于2.4.x版内?/b>
q一部分的启动过E在2.4.x内核中简化了不少Q缺省的独立初始化过E只剩下|络Qsock_init()Q和创徏事g理核心U程Q而其他所需要的初始化都使用__initcall()宏包含在do_initcalls()函数中启动执行?
回页?/font> initq程是系l所有进E的LQ内核在完成核内引导以后Q即在本U程Q进E)I间内加载initE序Q它的进E号??/p>
initE序需要读?etc/inittab文g作ؓ其行为指针,inittab是以行ؓ单位的描q性(非执行性)文本Q每一个指令行都具有以下格式:
id:runlevel:action:process其中id为入口标识符Qrunlevel行别,action为动作代Pprocess为具体的执行E序?/p>
id一般要?个字W以内,对于getty或其他loginE序,要求id与tty的编L同,否则gettyE序不能正常工作?/p>
runlevel是init所处于的运行别的标识Q一般?Q?以及S或s???q行U别被系l保留,0作ؓshutdown动作Q?作ؓ重启臛_用户模式Q?为重启;S和s意义相同Q表C单用户模式Q且无需inittab文gQ因此也不在inittab中出玎ͼ实际上,q入单用h式时Qinit直接在控制台Q?dev/consoleQ上q行/sbin/sulogin?/p>
在一般的pȝ实现中,都用了2???几个U别Q在Redhatpȝ中,2表示无NFS支持的多用户模式Q?表示完全多用h式(也是最常用的别)Q?保留l用戯定义Q?表示XDM囑Şd方式?Q?U别也是可以使用的,传统的Unixpȝ没有定义q几个别。runlevel可以是ƈ列的多个|以匹配多个运行别,对大多数action来说Q仅当runlevel与当前运行别匹配成功才会执行?/p>
initdefault是一个特D的action|用于标识~省的启动别;当init由核心激zM后,它将dinittab中的initdefault,取得其中的runlevelQƈ作ؓ当前的运行别。如果没有inittab文gQ或者其中没有initdefault,init在控制Ch输入 runlevel?/p>
sysinit、boot、bootwait{action在pȝ启动时无条gq行Q而忽略其中的runlevelQ其余的actionQ不含initdefaultQ都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描q?/p>
在Redhatpȝ中,一般情况下inittab都会有如下几:
id:3:initdefault: #表示当前~省q行U别?--完全多Q务模式; si::sysinit:/etc/rc.d/rc.sysinit #启动时自动执?etc/rc.d/rc.sysinit脚本 l3:3:wait:/etc/rc.d/rc 3 #当运行别ؓ3Ӟ?为参数运?etc/rc.d/rc脚本Qinit等待其q回 0:12345:respawn:/sbin/mingetty tty0 #?Q?各个U别上以tty0为参数执?sbin/mingettyE序Q打开tty0l端用于 #用户dQ如果进E退出则再次q行mingettyE序 x:5:respawn:/usr/bin/X11/xdm -nodaemon #?U别上运行xdmE序Q提供xdm囑Ş方式d界面Qƈ在退出时重新执行
回页?/font> 上一节已l提到initq程启动运行rc脚本Q这一节将介绍rc脚本具体的工作?/p>
一般情况下Qrc启动脚本都位?etc/rc.d目录下,rc.sysinit中最常见的动作就是激zM换分区,查磁盘,加蝲g模块Q这些动作无论哪个运行别都是需要优先执行的。仅当rc.sysinit执行完以后init才会执行其他的boot或bootwait动作?/p>
如果没有其他boot、bootwait动作Q在q行U别3下,/etc/rc.d/rc会得到执行Q命令行参数?Q即执行/etc/rc.d/rc3.d/目录下的所有文件。rc3.d下的文g都是指向/etc/rc.d/init.d/目录下各个Shell脚本的符可接,而这些脚本一般能接受start、stop、restart、status{参数。rc脚本以start参数启动所有以S开头的脚本Q在此之前,如果相应的脚本也存在K打头的链接,而且已经处于q行态了Q以/var/lock/subsys/下的文g作ؓ标志Q,则将首先启动K开头的脚本Q以stop作ؓ参数停止q些已经启动了的服务Q然后再重新q行。显Ӟq样做的直接目的是当init改变q行U别Ӟ所有相关的服务都将重启Q即使是同一个别?/p>
rcE序执行完毕后,pȝ环境已经讄好了Q下面就该用L录系l了?/p>
回页?/font> 在rcq回后,init得到控Ӟq启动mingettyQ见W五节)。mingetty是getty的简化,不能处理串口操作。getty的功能一般包括:
- 打开l端U,q设|模?
- 输出d界面及提C,接受用户名的输入
- 以该用户名作为login的参敎ͼ加蝲loginE序
注:用于q程d的提CZ息位?etc/issue.net中?/p>
loginE序在getty的同一个进E空间中q行Q接受getty传来的用户名参数作ؓd的用户名?/p>
如果用户名不是rootQ且存在/etc/nologin文gQlogin输出nologin文g的内容,然后退出。这通常用来pȝl护旉止非root用户d?/p>
只有/etc/securetty中登C的终端才允许root用户dQ如果不存在q个文gQ则root可以在Q何终端上d?etc/usertty文g用于对用户作出附加访问限Ӟ如果不存在这个文Ӟ则没有其他限制?/p>
当用L录通过了这些检查后Qlogin搜?etc/passwd文gQ必要时搜烦 /etc/shadow文gQ用于匹配密码、设|主目录和加载shell。如果没有指定主目录Q将默认为根目录Q如果没有指定shellQ将默认?bin/sh。在控制{交给shell以前Q?getty输?var/log/lastlog中记录的上次dpȝ的信息,然后查用h否有新邮Ӟ/usr/spool/mail/{username}Q。在讄好shell的uid、gidQ以及TERMQPATH {环境变量以后,q程加蝲shellQlogin的Q务也完成了?/p>
回页?/font> q行U别3下的用户login以后Q将启动一个用h定的shellQ以下以/bin/bashZl箋我们的启动过E?/p>
bash是Bourne Shell的GNU扩展Q除了承了sh的所有特点以外,q增加了很多Ҏ和功能。由login启动的bash是作Z个登录shell启动的,它承了getty讄的TERM、PATH{环境变量,其中PATH对于普通用户ؓ"/bin:/usr/bin:/usr/local/bin"Q对于root ?/sbin:/bin:/usr/sbin:/usr/bin"。作为登录shellQ它首先寻?etc/profile 脚本文gQƈ执行它;然后如果存在~/.bash_profileQ则执行它,否则执行 ~/.bash_loginQ如果该文g也不存在Q则执行~/.profile文g。然后bash作Z个交互式shell执行~/.bashrc文gQ如果存在的话)Q很多系l中Q~/.bashrc都将启动 /etc/bashrc作ؓpȝ范围内的配置文g?/p>
当显C出命o行提C符的时候,整个启动q程q束了。此时的pȝQ运行着内核Q运行着几个核心U程Q运行着initq程Q运行着一批由rc启动脚本Ȁzȝ守护q程Q如 inetd{)Q运行着一个bash作ؓ用户的命令解释器?/p>
回页?/font> 如果~省q行U别设ؓ5Q则pȝ中不光有1-6个getty监听着文本l端Q还有启动了一个XDM的图形登录窗口。登录过E和文本方式差不多,也需要提供用户名和口令,XDM 的配|文件缺省ؓ/usr/X11R6/lib/X11/xdm/xdm-config文gQ其中指定了 /usr/X11R6/lib/X11/xdm/xsession作ؓXDM的会话描q脚本。登录成功后QXDM执行这个脚本以q行一个会话管理器Q比如gnome-session{?/p>
除了XDM以外Q不同的H口理pȝQ如KDE和GNOMEQ都提供了一个XDM的替代品Q如gdm和kdmQ这些程序的功能和XDM都差不多?br />
杨沙zԌP现攻d防科大计机学院计算Y件方向博士学位。您可以通过电子邮g pubb@163.net跟他联系?
]]>
2. Attach shared memory
char *buf = shmat (shm_id, 0, 0);
3. Read / Write shared memory
sharedbuf->size = size_;
memcpy(sharedbuf->buf, mybuf, size_);
memcpy(mybuf, sharedbuf->buf, sharedbuf->size);
3. Detach shared memory (optional)
shmdt (buf);
4. Remove shared memory
if (shmctl (shm_id, IPC_RMID, (struct shmid_ds *)0) < 0)
perror ("shmctl");
一个测试过的实?br />#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define SHM_MODE (SHM_R | SHM_W)
#define SHM_SIZE 2048
int main()
{
int segment_id, segment_size;
char *shared_memory;
pid_t pid;
if((segment_id = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)/*获得׃n内存标识W?/
perror("shmget error!\n");
if((shared_memory = shmat(segment_id, 0, 0)) == (void *)-1)/*q程和共享内存段相连?/
perror("shmat error!\n");
printf("test1 send a message to share memory.\n");
sprintf(shared_memory, "Hello test2\n");
shmdt(shared_memory);/*q链接*/
pid = fork();
if(pid < 0)
perror("Creating process error!\n");
else if(pid > 0)
{
wait(NULL); /*父进E等待子q程l束*/
shmctl(segment_id, IPC_RMID, 0);/*子进E结束,父进E将׃n内存删除*/
exit(0);
}
else
{
if((shared_memory = shmat(segment_id, 0, 0)) == (void*)-1)/*子进E和׃n内存q接*/
perror("shmat error!\n");
printf("test2 get a message form share memory:%s",shared_memory);
shmdt(shared_memory);
}
}
相关参考:http://blog.csdn.net/Apollo_zhc/archive/2006/06/01/768694.aspx
1. 获取文g的信息:
stat(char* filename, struct stat* buf);
struct stat {
dev_t st_dev; /* 讑֤ */
ino_t st_ino; /* 节点 */
mode_t st_mode; /* 模式 */
nlink_t st_nlink; /* 连?*/
uid_t st_uid; /* 用户ID */
gid_t st_gid; /* lID */
dev_t st_rdev; /* 讑֤cd */
off_t st_off; /* 文g字节?*/
unsigned long st_blksize; /* 块大?*/
unsigned long st_blocks; /* 块数 */
time_t st_atime; /* 最后一ơ访问时?*/
time_t st_mtime; /* 最后一ơ修Ҏ?*/
time_t st_ctime; /* 最后一ơ改变时?指属? */
};
struct statfs
{
long f_type; /* 文gpȝcd */
long f_bsize; /* 块大?/
long f_blocks; /* 块多?/
long f_bfree; /* I闲的块Q)*/
long f_bavail; /* 可用?*/
long f_files; /* L件节?*/
long f_ffree; /* I闲文g节点 */
fsid_t f_fsid; /* 文gpȝid */
long f_namelen; /* 文g名的最大长?*/
long f_spare[6]; /* spare for later */
};
2. 获取文g讉K权限或者判断文件是否存在:
int access(char* filename, int mode);
3. 获取当前旉Q?br />time_t t;char* asctime(localtime(&t));
或?br />time(&t);char* ctime(&t);
得到的字W串形式为:Wed Mar 12 10:07:53 2003
4. 计算两个时刻之间的时间差
double difftime(time_t time2, time_t time1);
5. 删除某文Ӟ
int unlink(char* pathname);
int remove(char* pathname);
6. 删除某目录:
int rmdir(const char* pathname);
7. 获得当前所在目录名Q?br />char * getcwd(char *buf,size_t size); buf会q回目前路径名称?
8. 获取目录信息Q?br />DIR * opendir(const char * pathname);
int closedir(DIR *dir);
struct dirent * readdir(DIR *dir);
struct dirent
{
long d_ino; /* inode number */
off_t d_off; /* offset to this dirent */
unsigned short d_reclen; /* length of this d_name */
char d_name [NAME_MAX+1]; /* file name (null-terminated) */
};
9. strerror(errno);函数会返回一个指定的错误L错误信息的字W串.
10.得到当前路径下面所有的文g(包含目录)的个?
struct dirent **namelist;
int num = scandir(".",&namelist,0,alphasort)
11./etc/ld.so.confQ包含共享库的搜索位|?
查看执行文g调用了哪些共享库
shell>ldd a.out
׃n库管理工P一般在更新了共享库之后要运行该命o
shell>ldconfig
12.查看文g执行的速度
shell>time ./a.out
13.改变文g讉K权限
int chmod(const char* path, mode_t mode);
14.改变文g大小
int chsize(int handle, long size);
15.把一个QҎ转换为字W串
char ecvt(double value, int ndigit, int *decpt, int *sign);
16.文件结?br />int eof(int *handle);
17.流上的文gl束W?
int feof(FILE *stream);
18.流上的错误
int ferror(FILE *stream);
19.装入q运行其它程序的函数
int execl(char *pathname, char *arg0, arg1, ..., argn, NULL);
int execle(char *pathname, char *arg0, arg1, ..., argn, NULL,
char *envp[]);
int execlp(char *pathname, char *arg0, arg1, .., NULL);
int execple(char *pathname, char *arg0, arg1, ..., NULL,
char *envp[]);
int execv(char *pathname, char *argv[]);
int execve(char *pathname, char *argv[], char *envp[]);
int execvp(char *pathname, char *argv[]);
int execvpe(char *pathname, char *argv[], char *envp[]);
20.指数函数
double exp(double x);
21. struct sockaddr
{
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
struct sockaddr_in
{
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct in_addr
{
unsigned long s_addr;
};
s_addr按照|络字节序存储IP地址
sin_zero是ؓ了让sockaddr与sockaddr_in两个数据l构保持大小相同而保留的I字节?/p>
使用的例子:
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(3490);
sa.sin_addr.s_addr = inet_addr("132.241.5.10");
bzero(&(sa.sin_zero), 8);
注意Q如果sa.sin_addr.s_addr Q?INADDR_ANYQ则不指定IP地址Q用于ServerE序Q?/p>
22. #define UNIX_PATH_MAX 108
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径?*/
};
23. IP地址转换函数:
unsigned long inet_addr (const char *cp);
inet_addr一个点分十q制IP地址字符串{换成32位数字表C的IP地址Q网l字节顺序)
char* inet_ntoa (struct in_addr in);
inet_ntoa一?2位数字表C的IP地址转换成点分十q制IP地址字符丌Ӏ?/p>
q两个函C为反函数
字节序转换:
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"
24. 获取当前机器的CPU、内存用情?br />getrusage
25. open的用中常用的flag和mode参数
int FILE_FLAG = O_WRONLY|O_APPEND|O_CREAT;
int FILE_MODE = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
26. makefile中常用的W号Q?br />预定义变?含义
$* 不包含扩展名的目标文件名U?br />$@ 目标的完整名U?br />$% 如果目标是归档成员,则该变量表示目标的归档成员名U。例如,如果目标名称
?mytarget.so(image.o)Q则 $@ ?mytarget.soQ?$% ?image.o?/p>
$+ 所有的依赖文gQ以I格分开Qƈ以出现的先后为序Q可能包含重复的依赖文g?br />$< W一个依赖文件的名称?br />$? 所有的依赖文gQ以I格分开Q这些依赖文件的修改日期比目标的创徏日期?br />$^ 所有的依赖文gQ以I格分开Q不包含重复的依赖文件?/p>
AR 归档l护E序的名Uͼ默认gؓ ar?br />ARFLAGS 归档l护E序的选项?br />AS 汇编E序的名Uͼ默认gؓ as?br />ASFLAGS 汇编E序的选项?br />CC C ~译器的名称Q默认gؓ cc?br />CCFLAGS C ~译器的选项?br />CPP C 预编译器的名Uͼ默认gؓ $(CC) -E?br />CPPFLAGS C 预编译的选项?br />CXX C++ ~译器的名称Q默认gؓ g++?br />CXXFLAGS C++ ~译器的选项?br />FC FORTRAN ~译器的名称Q默认gؓ f77?br />FFLAGS FORTRAN ~译器的选项?/p>
用变量object表示所有的.o文gQ?br />objects := $(wildcard *.o)
make -n或?-just-print表示只是昄命oQ但不会执行命o
make -s或?-slient表示全面止命o的显C?br />make -i或?-ignore-errors表示Makefile中所有命令都会忽略错?br />make -k或?-keep-going表示如果某规则中的命令出错了Q那么就l止该规则的执行Q但l箋执行其它规则
在makefile中直接利用shell获取变量PLAT
使用make中的一U用变量来定义变量的Ҏ。这U方法用的是?=”操作符
PLAT := $(shell uname -a)
我们要定义一个变量,其值是一个空|那么我们可以q样来:
nullstring :=
space := $(nullstring) # end of the line
FOO ?= bar含义?
如果FOO没有被定义过Q那么变量FOO的值就是“bar”,如果FOO先前被定义过Q那么这条语什么也不做Q其{h于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
foo := a.o b.o c.o
bar := $(foo:.o=.c)
我们先定义了一个?(foo)”变量,而第二行的意思是把?(foo)”中所有以?o”字东y结䏀全部替换成?c”,所以我们的?(bar)”的值就是“a.c b.c c.c”?/p>
你想在Makefile中设|参数|你可以用“override”指C符。其语法是:
override <variable> = <value>
override <variable> := <value>
当然Q你q可以追加:
override <variable> += <more text>
makeq行时的pȝ环境变量可以在make开始运行时被蝲入到Makefile文g中,
但是如果Makefile中已定义了这个变量,或是q个变量由make命o行带入,
那么pȝ的环境变量的值将被覆盖?br />如果make指定了?e”参敎ͼ那么Q系l环境变量将覆盖Makefile中定义的变量
|
|
|
|
|
|
|
|
|
|
一、基dQ?br />1QGCC
用哪个参数可以生obj文gQB
(A) -c (B)-o (C)-share (D)-static
2、GDB
在main函数处设|断点的命o是:A
(A)b main (B)set main (C)set 0 (D)b 0
3、UNP
下列函数中可以将L字节序{换成|络字节序的是:D
(A)convert() (B)sprintf() (C)ntonl() (D)htonl()
4、man
查阅"readpȝ调用"man手册的命令是QB
(A)man 1 read (B)man 2 read (C)man 3 read (D)man read
5、shell
删除"/tmp/prj/"目录下(及所有子目录下)所有名字以".o"l尾的文ӞA
(A)find /tmp/prj/ -name "*.o" | xargs -i rm {}
(B)rm -rf /tmp/prj/*.o
(C)find /tmp/prj/ -name "*.o" -rm {} \;
(D)find /tmp/prj/*.o -name "*.o" | xargs -i rm {}
6、IPC
下列哪些属于IPC范畴(多?QA,C,D,E,G,H
(A)信号
(B)文g监视
(C)道
(D)消息队列
(E)信号?br />(F)odbc
(G)׃n内存
(H)UNIX域套接字
7、Signal
下列那个信号不可以被捕获或阻塞:A
(A)SIGKILL (B)SIGINT (C)SIGCHILD (D)SIGUSR1
8、Thread
下列那一Ҏq是错误的:B
(A)q程拥有独立的内存空_而线E之间却׃n内存I间?br />(B)q程可以使用libc库,而线E不可以
(C)q程和线E在Linux内核中都使用clone()来实?br />(D)信号量也可以作ؓU程间的通讯手段
二、C/C++部分Q?br />1、以下ؓHP-UX下的64位应用程序,请写出其q行l果?
void func(char *ptr)
{
printf("%d\n", sizeof(ptr));
}
int main()
{
char buf[1024];
char *buf_p = buf;
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(buf));
func(buf);
func(buf_p);
}
{案Q?br />1
8
1024
8
8
2、请分析以下E序Qƈ写出其运行结果?br />char *get_memory(void)
{
char p[] = "hello world";
return p;
}
int main()
{
char *str = NULL;
str = get_memory();
printf(str);
}
{案Q?q是个典型有内存错误的程序,回答出有内存错误对Q最好能回答哪里有错)
3、请~写下面的C函数
/*
功能Q在堆上分配一块指定大的内存Qƈ且全部清0Q如果出错则q回一个空指针
lensQ请求分配内存的寸
*/
void *get_mem(int lens)
{
...
}
{案Q?考察~程风格和细心程度,不一定要和下面的E序一P注意出错处理和返回值控?
void *get_mem(int lens)
{
char *ret;
if (lens <= 0)
return NULL;
ret = malloc(lens);
if (ret == NULL)
return NULL;
bzero(ret, lens);
return ret;
}
4、Makefile
假设有一个小型web服务器程序名?miniweb",它的源代码包含三个源文g: utils.c, lib.c, main.c
请ؓq个E序~写一个Makefile?/p>
{案Q?考察是否会写MakefileQ可能很多h都不会写Q如果此题没回答出来Q面试的时候需要再ơ询问是否会用make和Makefile)
一个最基本的例子:
miniweb: main.o lib.o utils.o
cc -o $@ $<
*.o: *.c
cc -c $<
clean:
rm -f *.o rm miniweb
[语法]Q?ls [-RadCxmlnogrtucpFbqisf1] [目录或文?.....]
[说明]Q?ls 命o列出指定目录下的文gQ缺省目录ؓ当前目录 ./Q缺省输出顺序ؓU向按字W顺序排列?br />
-R 递归地列出每个子目录的内?/p>
-a 列出所有文Ӟ包括W一个字Wؓ?”的隐藏文g
-d 若后面参数是目录Q则只列出目录名而不列出目录内容Q常?l选项q?br />用以昄目录状态?/p>
-C 输出时多列显C?
-x 横向按字W顺序排?
-m 输出按流式格式横向排列,文g名之间用逗号(Q?分隔
-l 长列表输出,昄文g详细信息Q每行一个文Ӟ从左臛_依次是:
文g存取模式 链接?文g?文gl?文g字节?上次修改旉
其中文g存取模式?0个字母表C,从左臛_的意义如下:
W一个字母表C文件种c,可以是以下几U情况:
d 为目录文?
l 为链?
b 为块文g
c 为字W型文g
p 为命名管道(FIFO)
- 为普通文?
后面9个字母分别表C文件主、同l用戗其他用户对文g的权力,用r表示可读Qw 表示可写Qx 表示可执行。如果是讑֤文gQ则在文件字节数处显C:主设?从设备?
-n ?l选项相同Q只是文件主用数?即UID)昄Q文件组用数?
(即GID)表示
-o ?l选项相同Q只是不昄文gl?
-g ?l选项相同Q只是不昄文g?
-r 逆序排列
-t 按时间顺序排列而非按名?
-u 昄旉时用上ơ访问时间而非上次修改旉
-c 昄旉时用上ơ修改i节点旉而非上次修改旉
-p 若所列文件是目录文gQ则在其后显C斜?/)
-F 在目录文件后?/'Q在可执行文件后?*'
-b 文g名中若有非打印字W,则用八进制显C字符
-q 文g名中的打印字W用'?'表示
-i 昄节点?
-s 昄文g长度时用块长度而非字节长度
-f 后面的参数解释为目录ƈ列出其中的每一?
-1 每行仅列一?
[例子]:
ls 列出当前目录下的文g
ls -al /bin 以长列表的Ş式列出目?/bin 下的所有文Ӟ包括隐藏文g
1.2 pwd
[语法]: pwd
[说明]Q?本命令用于显C当前的工作目录
[例子]:
pwd 昄出当前的工作目录
1.3 cd
[语法]: cd [目录]
[说明]Q本命o用于改变当前的工作目录,无参数时使用环境变量$HOME 作ؓ其参敎ͼ$HOME 一般ؓ注册时进入的路径?
[例子]Q?
cd 回到注册q入时的目录
cd /tmp q入 /tmp 目录
cd ../ q入上目录
1.4 mkdir
[语法]: mkdir [-m 模式] [-p] 目录?
[说明]: 本命令用于徏立目录,目录的存取模式由掩码Qumask)军_Q要求对其父目录h写权限,目录的UID和GID为实际UID和GID
-m 按指定存取模式徏立目?
-p 建立目录时徏立其所有不存在的父目录
[例子]:
mkdir tmp 在当前目录下建立子目?tmp
mkdir -m 777 /tmp/abc 用所有用户可d写可执行的存取模?
建立目录 /tmp/aaa Q存取模式参看命?chmod
mkdir -p /tmp/a/b/c 建立目录 /tmp/a/b/c Q若不存在目?/tmp/a
?tmp/a/b 则徏立之
1.5 rmdir
[语法]: rmdir [-p] [-s] 目录?
[说明]: 本命令用于删除目?
-p 删除所有已lؓI的父目?
-s 当?p 选项Ӟ出现错误不提C?
[例子]:
rmdir /tmp/abc 删除目录 /tmp/abc
rmdir -p /tmp/a/b/c 删除目录 /tmp/a/b/c Q若目录 /tmp/a /b
?tmp/a I,则删?
1.6 cat
[语法]: cat [-u] [-s] [-v[-t] [-e]] 文g...
[说明]: 昄和连接一个或多个文gx准输?
-u 无缓冲的输出(~省为有~冲输出)
-s 对不存在的文件不作提C?
-v 昄出文件中的非打印字符Q控制字W显C成^n Qn为八q制数字Q?
其他非打印字W显C成M-x Q?x 字符?位的8q制数?
-t 在?v 选项Ӟ制表符QtabQ?昄?^IQ将换页W?
QformfeedQ显C成 ^ L
-e 在?v 选项Ӟ在每一行的行尾昄 $
[例子]:
cat file 昄文g
cat -s -v -e file1 file2 file3 逐个昄文g file1 file2 file3
1.7 head
[语法]: head [-n] [文g ...]
[说明]: 文件的头n 行显C?~省gؓ 10 行,昄多个文gӞ在每个文件的前面加上 ==> 文g?<==
[例子]Q?
head -9999 file1 file2 昄文g file1 ?file2 的头 9999 ?
1.8 more
[语法]: more [-cdflrsuw] [Q?行数] [+ 行数] [+ / 模式 ] [ 文g ... ]
[说明]: 文件显C在l端上,每次一屏,在左下部昄Q-moreQ-Q若是从文gd而非从管道,则在后面昄癑ֈ比,表示已显C的部分Q按回R键则上滚一行,按空格键则上滚一屏,未显C完时可以用more 命o中的子命令?
-c 昄文g之前先清?
-d 当输错命令时昄错误信息而不是响?bell)
-f 不折叠显C长的行
-l 不将分页控制W?CTRL D)当作늻?
-r 一般情况下Qmore 不显C控制符Q本选项使more 昄控制W,
例如Q将 (CTRL C) 昄?^ C
-s 多个空行{换成一个空行显C?
-u 止产生下划U序?
-w 一般情况下 more 昄完后立即推出Q本选项在显C完后作?
C,敲Q意键后推?
-n 行数 指定每屏昄的行?
+ 行号 从指定行号开始显C?
+/模式 在文件中搜烦指定模式Q从模式出现行的上两行开始显C?文g未显C完Ӟ可以使用more 命o中的子命令,命o中除? ?/ 以外均不回显Q也不用敲回车,当命令破?more 提示行时Q可用退格键恢复提示行。在以下子命令操作中Qi 表示数字Q缺省gؓ 1?
i I格 上滚一屏多 i ?
i 回R 上滚 i ?
i CTRL+D i ~省时上?11 行,否则上滚 i ?
id i ~省时上?11 行,否则上滚 i ?
iz i ~省时上滚一屏,否则定义每屏?i ?
is 跌 i 行后昄一?
if 跌 i 屏后昄一?
i CTRL+B 跛_ i 屏后昄一?
b 跛_ 一屏后昄一?
q ?Q 推出 more
= 昄当前行号
v 从当前行开始编辑当前文件编辑器q境变?
$EDITOR定义
h 昄帮助信息
i / 模式 向前搜烦Q直x式的W?i ơ出?Q?从该行的?两行开始显CZ?
in 向前搜烦Q直至上一模式的第 i ơ出?Q?从该?的上两行开始显CZ?
单引?回到上次搜烦的出发点Q若无搜索则回到开始位|?
! 命o ȀzM个sh L行指定的命o
i Q?n 跛_后面W?i 个文Ӟ若不存在则蟩到最后一个文?
Qf 昄当前文g名和行号
Qq ?QQ 推出 more
. (? 重复上次命o
[ 例子]:
more -c +50 file 清屏后,从第50行开始显C文?file
more -s -w file1 file2 file3 昄文g file1 file2 file3
1.9 cp
[语法]: cp [ -p ] [ -r ] 文g 1 [ 文g 2 ...] 目标
[说明]: 文?(文g2 ...)拯到目标上Q目标不能与文g同名Q?若目标是文g名,则拷贝的文g只能有一个,若目标是目录Q则拯的文件可以有多个Q若目标文g不存在,则徏立这个文Ӟ若存在,则覆盖其以前的内容,若目标是目录Q则文件拷贝到q个目录下?
- i 在覆盖已存在文g时作提示Q若回答 y 则覆盖,其他则中?
- p 不仅拯文g内容Q还有修Ҏ_存取模式Q存取控制表Q?但不拯
UID ?GID
- r 若文件名为目录,则拷贝目录下所有文件及子目录和它们的文Ӟ此时
目标必须为目?
[例子]:
cp file1 file2 文?file1 拯到文?file2
cp file1 file2 /tmp 文?file1 和文?file2 拯到目?/tmp ?
cp -r /tmp /mytmp 目?/tmp 下所有文件及其子目录拯至目?mytmp
1.10 mv
[语法]: mv [-f] [-i] 文g1 [文g2...] 目标
[说明]: 文件移动至目标Q若目标是文件名Q则相当于文件改?
- i 在覆盖已存在文g时作提示Q若回答 y 则覆盖,其他则中?
- f 覆盖前不作Q何提C?
[例子]:
mv file1 file2 文?file1 改名?file2
mv file1 file2 /tmp 文?file1 和文?file2 Ud到目?/tmp ?
1.11 rm
[语法]: rm [-f] [-i] 文g...
?rm -r [-f] [-i] 目录?.. [文g]
[说明]: 用来删除文g或目?
- f 删除文g时不作提C?
- r 递归地删除目录及其所有子目录
- i 删除文g之前先作提示
[例子]:
rm file1 删除文g file1
rm -i /tmp/* 删除目录 /tmp 下的所有文?
rm -r /mytmp 递归地删除目?/mytmp
1.12 chmod
[语法]: chmod [-R] 模式 文g...
?chmod [ugoa] {+|-|=} [rwxst] 文g...
[说明]: 改变文g的存取模式,存取模式可表CZؓ数字或符号串Q例如:
chmod nnnn file Q?n?-7的数字,意义如下:
4000 q行时可改变UID
2000 q行时可改变GID
1000 |粘着?
0400 文gd?
0200 文gd?
0100 文gd执行
0040 同组用户可读
0020 同组用户可写
0010 同组用户可执?
0004 其他用户可读
0002 其他用户可写
0001 其他用户可执?
nnnn 是上列数字相加得到的,例如 chmod 0777 file 是指文?file 存取权限|ؓ所有用户可d写可执行?
-R 递归地改变所有子目录下所有文件的存取模式
u 文g?
g 同组用户
o 其他用户
a 所有用?
+ 增加后列权限
- 取消后列权限
= |成后列权限
r 可读
w 可写
x 可执?
s q行时可|UID
t q行时可|GID
[例子]:
chmod 0666 file1 file2 文?file1 ?file2 |ؓ所有用户可d?
chmod u+x file Ҏ?file 增加文gd执行权限
chmod o-rwx Ҏ件file 取消其他用户的所有权?
1.13 chown
[语法]: chown [-R] 文g?文g...
[说明]: 文g的UID表示文g的文件主Q文件主可用数字表示Q?也可用一个有效的用户名表C,此命令改变一个文件的UIDQ仅当此文g的文件主或超U用户可使用?
-R 递归地改变所有子目录下所有文件的存取模式
[例子]:
chown mary file 文?file 的文件主改ؓ mary
chown 150 file 文?file 的UID改ؓ150
1.14 chgrp
[语法]: chgrp [-R] 文gl?文g...
[说明]Q?文g的GID表示文g的文件组Q文件组可用数字表示Q也可用一个有效的l名表示Q此命o改变一个文件的GIDQ可参看chown?
-R 递归地改变所有子目录下所有文件的存取模式
[例子]:
chgrp group file 文?file 的文件组改ؓ group
1.15 cmp
[语法]: cmp [-l] [-s] 文g1 文g2
[说明]: 比较两个文gQ若文g1 ?"-" Q则使用标准输入Q?两个文g相同则无提示Q不同则昄出现W一个不同时的字W数和行受?
-l 昄每个不同处的字节?10q制)和不同的字节(8q制)
-s 不作M提示Q只q回?
[例子]:
cmp file1 file2 比较文g file1 ?file2
cmp -l file1 file2 比较文gfile1 ?file2 的每处不?
1.16 diff
[语法]: diff [-be] 文g1 文g2
[说明]: 本命令比较两个文本文Ӟ不同的行列出来
-b 一串空格或TAB转换成一个空格或TAB
-e 生成一个编辑角本,作ؓex或ed的输入可文?转换成文?
[例子]:
diff file1 file2
diff -b file1 file2
diff -e file1 file2 >edscript
1.17 wc
[语法]: wc [-lwc] 文g...
[说明]: l计文g的行、字、字W数Q若无指定文Ӟ则统计标准输?
-l 只统计行?
-w 只统计字?
-c 只统计字W数
[例子]:
wc -l file1 file2 l计文gfile1和file2 的行?
1.18 split
[语法]: split [-n] [ 文g [名字]]
[说明]: split 指定大文g分解q个文Ӟ每个文g长度为n?n ~省时ؓ1000)Q第一个小文g名ؓ指定的名字后跟aaQ直至zzQ名字缺省gؓxQ若未指定大文g名,则用标准输?
[例子]:
split -500 largefile little
文件largefile ?00行写入一个文ӞW一个文件名为littleaa
1.19 touch
[语法]: touch [-amc] [mmddhhmm[yy]] 文g...
[说明]: 指定文件的讉K旉和修Ҏ间改变,若指定文件不存在则创ZQ若无指定时_则用当前时_q回值是未成功改变时间的文g个数Q包括不存在而又未能创徏的文件?
-a 只改变访问时?
-m 只改变修Ҏ?
-c 若文件不存在Q不创徏它且不作提示
mmddhhmm[yy] 两位表示 月日时分[q]
[例子]:
touch file
更新文gfile的时?
touch 0701000097 HongKong
文件HongKong的时间改?7q?????
1.20 file
[语法]: file [-f 文g名文件] 文g...
[说明]: file Ҏ定文件进行测试,量猜测出文件类型ƈ昄出来
-f 文g名文?文g名文件是一个包含了文g名的文本文gQ?-f 选项试
文g名文件中所列出的文?
[例子]:
file * 昄当前目录下所有文件的cd
1.21 pack
[语法]: pack 文g...
[说明]: pack 指定文件{储ؓ压羃格式Q文件名后加 ".z "Q?文g存取模式Q访问时_修改旉{均不变
[例子]:
pack largefile largefile 压羃后{储ؓlargefile.z
1.22 pcat 昄压羃文g
[语法]: pcat 文g...
[说明]: pcat 昄输出压羃文g
[例子]:
pcat largefile.z 昄压羃前的largefile
pcat largefile.z > oldfile 昄压羃前的laregfileQƈ其重定向到
文goldfile?
1.23 unpack
[语法]: unpack 文g...
[说明]: 压~后的文件解压后转储为压~前的格?
[例子]:
unpack largefile.z 压~文件largefile.z解压后{储ؓlargefile
1.24 find
[语法]: find 路径?.. 表达?
[说明]: find 命o递归地遍历指定\径下的每个文件和子目录,看该文g是否能表达式gؓ真,以下 n 代表一个十q制整数Q?n 代表打印 n Q?-n 代表于 n Q下面是合法表达式说明:
-name 模式 文g名与模式匚w则ؓ真,(\ {意符)
-perm [-]八进制数 文g存取模式与八q制数相同则为真若有- 选项Q则文g?
取模式含有八q制数规定模式即为真
-size n[c] 文g块长度ؓ n 则真(一块ؓ512字节)Q若
有c 选项Q则文g字节长度?n 则真
-atime n 若文件的最q访问时间ؓ n 天前则ؓ真,
find 命o改变其讉K的目录的讉K旉
-mtime n 若文件的最q修Ҏ间ؓ n 天前则ؓ?
-ctime n 若文件状态ؓ n 天前改变则ؓ?
-exec 命o { }\; 若命令返回gؓ0则真Q{ }内ؓ命o参数Q?
此命令必M \; 为结?
-ok 命o { }\; ?exec 相同Q只是在命o执行前先提示Q若
回答 y 则执行命?
-print 昄输出使表辑ּ为真的文件名
-newer 文g 若文件的讉K旉比newer 指定的文件新则真
-depth 先下降到搜烦目录的子目录Q然后才臛_自n
-mount 仅查扑含指定目录的文gpȝ
-local 文g在当前文件系l时为真
-type c 文gcd?c 则真Qc 取值可?b(块文? c (字符文g)
d(目录) l (W号链接) p (命名道) f (普通文?
\( 表达?\) 表达式ؓ真则?
-links n 文g链接Cؓ n 时ؓ?
-user 用户 当文件属于用h为真Q用户可用数字表CUID
-nouser 当文件不属于 /etc/passwd 中的一个用h为真
-group 文gl?当文件属于文件组时ؓ真,文gl可用数字表CGID
-nogroup 当文件不属于 /etc/group 中的一个组时ؓ?
-fstype cd 当文件所属文件系l类型ؓ指定cd时真
-inum n 当文?i 节点号ؓ n 时ؓ?
-prune 当目录名与模式匹配时Q不再搜索其子目?
可以用逻辑操作W将单表辑ּq接成复杂表辑ּ
逻辑操作W有 ! 表示非操作, -o 表示或操作,两个表达式ƈ列则表示
与操?
[例子]:
find / -name find* -print
从根目录开始搜索文件名?find* 的文件ƈ昄?
find ./ -exec sleep{1}\; -print
每秒昄一个当前目录下的文?
find $HOME \(-name a.out -o -name '*.o' \) -atime +7 -exec rm {} \;
?HOME目录开始搜索,删除所有文件名为a.out ?*.o 且访问时间在7天前的文?
1.25 grep
[语法]: grep [选项] 模式 [文g...]
[说明]: 在指定的文g中搜索模式,q显C所有包含模式的行,模式是一个正规表辑ּQ在使用正规表达式时Q最好将其引在单引号(') 中,若指定文件ؓ~省Q则使用标准输入Q正规表辑ּ可以是:
. 匚wL一个字W?
* 匚w0个或多个*前的字符
^ 匚w行开?
$ 匚w行结?
[] 匚w[ ]中的L一个字W,[]中可?- 表示范围Q?
例如[a-z]表示字母a 至z 中的L一?
\ 转意字符
命o中的选项为:
-b 昄块号
-c 仅显C各指定文g中包含模式的总行?
-i 模式中字母不区分大小?
-h 不将包含模式的文件名昄在该行上
-l 仅显C包含模式的文g?
-n 昄模式所在行的行?
-s 指定文g若不存在或不可读Q不提示错误信息
-v 昄所有不包含模式的行
[例子]:
grep 'good' * 在所有文件中搜烦含有字符?good 的行
grep '^myline' mytext 在文件mytext中搜索行首出现myline字符串的?
1.26 vi
[语法]Qvi [-wn] [-R] 文g...
[说明]: vi 是一个基于行~辑?ex 上的全屏q编辑器Q可以在vi 中?exQed的全部命令,vi选项?-wn 指将~辑H口大小|ؓn行,-R 为将~辑的文件置为只L式, vi 工作模式分ؓ命o模式和输入模式,一般情况下在命令模式下Q可敲入vi命oQ进入输入模式下时可以编辑要~辑的文本,命o a A i I o O c C s S R 可进入输入模式,在输入模式下?ESC 键可推出输入模式Q回到命令模式,在命令模式中敲入Q命令,则可q入ex方式Q在屏幕底部出现提示W?Q?Q此时可使用Lex命oQ屏q底行也用来? ? ! 命o的提CQ大多数命o可以在其前面加数字,表示命o执行的重复次敎ͼ下面单介l一下vi 的命令集Q^ 表示(CTRL)?
^B 退回前一,前面加数字表C重复次敎ͼ每次换页?
保留上一늚两行
^D 在命令模式下Q表CZ滚屏q的一半,在输入模式下Q表C回退?
左边的自动羃q处
^E 昄屏幕底线之下的一?
^F 前进一,前面加数字表C重复次敎ͼ每次换页?
保留上一늚两行
^G 昄当前文g名,当前行号和文件总行敎ͼq用癑ֈ号当前行?
整个文g中的位置
^H(退? 在命令模式下Q光标左UM|在输入模式下Q删d面的字符
^I(TAB) 在输入模式下Q生一串空?
^J(LF) 光标下移一?
^L h屏幕Q即屏q重新显C?
^M(回R) 在命令模式下Q光标移动至下行开?
在输入模式下Q开辟一新行
^N 光标下移一?
^P 光标上移一?
^Q 在输入模式下Q将其后的非打印字符作ؓ正文插入
^R h屏幕
^U 屏幕上滚一半,前面加数字时表示上滚的行敎ͼ此数字对
以后的^D ^U 命o有效
^V 在输入模式下Q将其后的非打印字符作ؓ正文插入
^W 在输入模式下Q光标回退一个字
^Y 昄屏幕底线之上的一?
^Z 暂停~辑Q退回上层Shell
^[(ESC) 退入模式,回到命o模式
! 暂时退出编辑,执行Shell命o
"(双引? 用于标志有名~冲区,~号~冲?-9用于保存被删ȝ正文Q字
母名~冲区a-z供用户存放自定义的正?
$ 光标移动到当前行尾Q前加数字则表示前移行数Q如2$表示Ud
C一行行?
% 光标移动到配对的小括号()或大括号{}上去
( 退回句子开?
) 前移到句子开?
- 退C一行第一个非I格字符
. 重复上一ơ改变缓冲区内容的命?
/ 模式 向前搜烦模式Q将光标Ud到模式出现处Q模式是一个正?
表达式,(参看 grep)
Q?在屏q底部提C:Q其后可使用ex命o
? 功能?/ Q但方向是向前查?
[[ 光标回退臛_一节分界处
\ 转意W?
]] 光标前移臌分界?
^(不是CTRL) 光标U至当前行第一个非I字W上
' q箋两个''表示光标移臛_Ud前的位置Q?后跟字母表示光标?
母标记的行首(参看 m 命o)
A 在行插入正文,q入输入模式
B 光标回退一个字
C 替换光标后的内容
D 删除光标后的内容
E 光标前移到字?
F 字符 在当前行向左查找指定字符
G 光标Ud到其前面数字指定的行Q若未指定则Ud到最后一?
H 光标Ud到屏q顶行,若前面有数字Q则Ud到屏q上该数?
指定的行
I 在行开头插入正?
J q接两行Q若前面有数字则q接数字指定的行
L 光标Ud到屏q底行,若前面有数字Q则Ud到屏q底U往上数?
数字指定的行
M 光标Ud到屏q中U?
N 使用模式查找/?Ӟ重复找下一个匹配的模式Q但方向与上ơ相
反,其功能同 n Q但方向相反
O 在当前行上开辟一新行
P 上ơ被删除的正文插入光标前面,可在其前面加~冲区编P~?
?-9用于保存被删ȝ正文Q字母名~冲区a-z供用户存放自?
义的正文
Q 从vi 推出q入ex命o状?
R 替换字符?
S 替换整行
T 字符 向左查找字符
U 当前行恢复至第一ơ修改前的状?
W 光标U至下一个字?
X 删除光标前的字符
Y 当前行存入无名~冲区,前面加数字表C存入的行数Q也可用?
名缓冲区来保存,以后可用命op或P其取出
ZZ 存盘退出vi
a 光标后插入正?
b 光标回退至上一个字?
cw 替换当前?
c) 替换当前句子
dw 删除一个字
dd 删除一?
e 光标Ud下一个字?
f 字符 在当前行向前查找字符
h 光标左移一?
i 在光标前插入正文
j 光标下移一?
k 光标上移一?
l 光标右移一?
m 字母 用字母标记当前行Q以后可?'字母使光标移动到当前行,
(参看'命o)
n 重复上次 / ?? 命o
o 在当前行下开辟一新行
p 用L冲区内容攑ֈ光标位置(参看P命o)
r 替换当前字符
s 用一串字W替换当前字W?
t 字符 光标Ud臛_W前
u 取消上次操作
w 光标U至下一字首
x 删除当前字符
yw 当前字存入无名~冲区,前面可加"xQ表C存入名字ؓx的有?
~冲?x为a-z)Q也可加数字表示存入的字敎ͼ以后可用P或p?
令取?
yy 当前行存入无名~冲区,用法参看yw
{ 光标Ud臛_一D开?
| 光标U至行首Q若前面加数字,则移到数字指定行的行?
} 光标U至下一D开?
在:提示W下Q常用命令如?
Qw 当前文g存盘
Qw! 强制存盘
Qw 文g 内容写入指定文?
Qw! 文g 强制写入指定文g
QxQy w 文g ?x?y 行写入指定文件中
Qr 文g 文件读到光标位|?
Qr ! 命o 系l命令的输出d光标位置
Qq 退出编?
Qq! 强制退?
Qx 与命令ZZ相同
Qe 文g?~辑另一文g
Qe ! 重新~辑文gQ放弃Q何改?
Qsh 执行shQ结束后回到~辑
Q? 命o 执行命o后回到编?
Qn ~辑下一文g
Qn 文g?重新定义待编辑文件表
Qset 讄 vi 的选项Q例?set nu 表示每行前显CP在选项?
加no则表C清除该选项Q例?set nonu 表示每行前不昄?
P下面是一些常用的选项:
ai 自动~进
aw ~辑下一文g前自动存?
ic 查找字符串时不区分大写
nu 每行前显C?
sm 输入)及}时显CZ之配对的( ?{
slow 插入时gq屏q刷?
ws 使查找能l过文g从头进?
wa 写文件之前不作对文g的检?/p>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=742217