1. OpenGL 渲染管線
OpenGL渲染管線分為兩大部分,模型觀測變換(ModelView Transformation)和投影變換(Projection Transformation)。做個比喻,計算機圖形開發就像我們照相一樣,目的就是把真實的場景在一張照相紙上表現出來。那么觀測變換的過程就像是我們擺設相機的位置,選擇好要照的物體,擺好物體的造型。而投影變換就像相機把真實的三維場景顯示在相紙上一樣。下面就分別詳細的講一下這兩個過程。
1.1模型觀測變換
讓我們先來弄清楚OpenGL中的渲染管線。管線是一個抽象的概念,之所以稱之為管線是因為顯卡在處理數據的時候是按照一個固定的順序來的,而且嚴格按照這個順序。就像水從一根管子的一端流到另一端,這個順序是不能打破的。先來看看下面的圖1:
圖1 OPENGL渲染管線
圖中顯示了OpenGL圖形管線的主要部分,也是我們在進行圖形編程的時候常常要用到的部分。一個頂點數據從圖的左上角(MC)進入管線,最后從圖的右下角(DC)輸出。MC是Model Coordinate的簡寫,表示模型坐標。DC是Device Coordinate的簡寫,表示設備坐標。 當然DC有很多了,什么顯示器,打印機等等。這里DC我們就理解成常說的屏幕坐標好了。MC當然就是3D坐標了(注意:我說的3D坐標,而不是世界坐 標),這個3D坐標就是模型坐標,也說成本地坐標(相對于世界坐標)。MC要經過模型變換(Modeling Transformation)才變換到世界坐標,圖2:
圖2 世界坐標系和模型坐標系
變換到世界坐標WC(World Coordinate)說簡單點就是如何用世界坐標系來表示本地坐標系中的坐標。為了講得更清楚一些,這里舉個2D的例子。如圖3:
圖3 世界坐標系和模型坐標系的計算
圖中紅色坐標系是世界坐標系WC,綠色的是模型坐標系MC。現在有一個頂點,在模型坐標系中的坐標為(1,1),現在要把這個模型坐標轉換到世界坐標中來表示。從圖中可以看出,點(1,1)在世界坐標系中的坐標為(3,4),現在我們來通過計算得到我們希望的結果。首先我們要把模型坐標系MC在世界坐標系中表示出來,使用齊次坐標(Homogeneous Coordinate )可以表示為矩陣(注意,本教程中使用的矩陣都是以列向量組成):
其中,矩陣的第一列為MC中x軸在WC中的向量表示,第二列為MC中y軸WC中的向量表示,第三列為MC中的原點在WC中的坐標。對齊次坐標系不了解的同學,請先學習游戲數學方面的知識。有了這個模型變換矩陣后,用這個矩陣乘以在MC中表示的坐標就可以得到該坐標在世界坐標系中的坐標。所以該矩陣和MC中的坐標(1,1)相乘有:
這也正是我們需要的結果。現在讓我們把相機坐標也加進去,相機坐標也稱為觀測坐標(View Coordinate),如圖4和圖5。
圖4 ModelView變換的三個坐標系
圖5 ModelView變換計算
來看看MC坐標中的點(1,1)如何在相機坐標中表示。從圖5中可以直接看出MC中的點(1,1)在相機坐標系VC中為(-2,-2)。和上面同樣的道理,我們可以寫出相機坐標系VC在世界標系WC中可以表示為:
那么世界坐標系中的點轉換為相機坐標系中的點我們就需求VC的逆矩陣:
那么世界坐標系WC中的點(3,4)在相機坐標系VC中坐標為:
上面的變換過程,就是可以把模型坐標變換為相機坐標。在OpenGL中,當我們申明頂點的時候,有時候說的是世界坐標,這是因為初始化的時候世界坐標系、模型坐標系和相機坐標系是一樣的,重合在一起的。所以OpenGL中提供了模型觀測變換,它是把模型坐標系直接轉換為相機坐標系,如圖4。現在我們已經計算得到了VC-1和MC,如果把VC-1和 MC相乘,就可以得到模型坐標在相機坐標中的表示。為了得到模型坐標系中的坐標在相機坐標系中的表示,這就是OpenGL中的ModelView變換矩 陣。這也是ModelView變換的名字的由來,它是通過了上面兩個步驟得到的。那么這里,ModelView變換矩陣M為:
現在只要用上面的模型觀測矩陣M乘以模型坐標系MC中的坐標就可以得到相機坐標系中的坐標了。模型觀測變換的關鍵就是要得到相機坐標系中的坐標,因為光照等計算都是在這個這個坐標系中完成的。下面我們實際OpenGL程序中檢查一下。在程序中,為了計算方便,我們使用圖6中的模型。
圖6 ModelView變換計算模型
根據圖中的數據,我們分別可以寫出對應MC和VC-1,從而求得觀測變換矩陣M。
現在程序中用glGetFloatv()這個函數來獲得當前矩陣數據來檢查一下。
- float m[16] = {0}; //用來保存當前矩陣數據
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glGetFloatv(GL_MODELVIEW_MATRIX, m);
- //相機設置,View 變換
- gluLookAt(0.0, 0.0, 5.0,
- 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0);
- glGetFloatv(GL_MODELVIEW_MATRIX, m);
- //投影設置
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(-10,10,-10,10,-10,10);
- glMatrixMode(GL_MODELVIEW);
- //Modeling變換
- glTranslatef(0, 0, -3);
- glGetFloatv(GL_MODELVIEW_MATRIX, m);
- glBegin(GL_POINTS);
- glVertex3f(1,1,0);
- glEnd();
如果在上面程序段中最后一個glGetFloatv(GL_MODELVIEW_MATRIX, m)處設定斷點的話,就可以看到圖7所顯示的數據。
圖7 ModelView變換矩陣數據
到這里,整個ModelView變換就完成了。通過ModelView變換后得到是相機坐標系內的坐標。在這個坐標系內典型的計算就是法線了。現在再來看看后面一個階段。
1.2投影變換
先還是復習一下OpenGL的渲染管線。圖1中可以看到,在投影變換(Projection Transformation)中也分為兩個部分,第一個部分是將上個階段得到的坐標轉換為平面坐標,第二個部分是將轉換后的平面坐標在進行歸一化并進行剪裁。一般地,將三維坐標轉換為平面坐標有兩種投影方式:正交投影(Orthogonal Projection)和透視投影(Perspective Projection)。
1.2.1 正交投影
正交投影很簡單,如圖8,對于三維空間中的坐標點和一個二維平面,要在對應的平面上投影,只需將非該平面上的點的坐標分量改為該平面上的坐標值,其余坐標不變。
圖8 正交投影
比如將點(1,1,5)正交投影到z=0的平面上,那么投影后的坐標為(1,1,0)。在openGL中,設置正交投影可以使用函數:
- glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
該函數可以設置正交投影的投影空間,在該空間以外的坐標點就不會被投影到投影平面上。函數中的六個參數分是投影空間六個平面,如圖9:
圖9 OpenGL正交投影空間和投影變換
在圖9中,大的投影空間是根據這六個參數設置的投影空間,OpenGL會自動將該空間歸一化,也就是將該空間或立方體轉化為變長為1的正六面體投影 空間,并且該證六面體的中心在相機坐標系的原點。一旦設置使用glortho函數設置投影空間,OpenGL會生成投影矩陣。這個矩陣的作用就是將坐標進 行正交投影并且將投影后的坐標正規化(轉換到-1到1之間)。要注意的是,生成該矩陣的時候,OpenGL會把右手坐標系轉換為左手坐標系。原因很簡單, 右手坐標系的Z軸向平面外的,這樣不符合我們的習慣。該矩陣的矩陣推導這里就不詳細說明了,不了解的同學可以參考游戲數學方面資料,這里只給出正交投影矩 陣。
這個矩陣看來很復雜,其實計算很簡單。舉個例子,現在設置了這樣的正交投影空間glOrtho(-10,10,-10,10,-10,10),這是個正六面體空間,變長為10。把這些參數帶入上面的矩陣可以得到
現在還是在OpenGL程序中來檢查一下。在OpenGL程序中添加下面代碼段:
- //投影設置
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(-10,10,-10,10,-10,10);
- glMatrixMode(GL_MODELVIEW);
- glGetFloatv(GL_PROJECTION_MATRIX,m)
在glGetFloatv(GL_PROJECTION_MATRIX,m)處設定斷點就可以看到圖10中所顯示的信息。
圖10 正交變換矩陣數據
1.2.2透視投影
透視投影和正交投影最大的區別就是透視投影具有遠近感。
圖11 透視投影
透視投影采用了圖11中的模型,這樣的模型就是保證遠的物體看起來小,近的物體看起來大。 在OpenGL中設置透視投影可以使用函數:
- void APIENTRY gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
該函數也會根據給定的參數生成一個投影空間。如圖11中,該投影空間是一個截頭體。同樣地,OpenGL會自動生成透視投影矩陣,該矩陣也會讓3D坐標投影在投影平面上,并且將投影后的坐標也進行正規化。下面也直接給出OpenGL中使用的透視投影矩陣。
下面在OpenGL中添加下面代碼段:
- //投影設置
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(45, 1.0, 1.0, 100);
- glMatrixMode(GL_MODELVIEW);
- glGetFloatv(GL_PROJECTION_MATRIX,m)
設置斷點后,我們可以看到圖12中顯示的數據。
圖12 透視變換矩陣數據
到此為止,整個投影變換就完成了。透過投影變換后得到的是正規化的投影平面坐標。這為下一個階段的視口變換(View port Transformation)做好了準備。
1.3視口變換
現在到了最后一個階段了。這個階段叫做視口變換,它把上個階段得到的正規化的投影坐標轉化為windows 窗口坐標。視口變換會將投影平面上的畫面映射到窗口上。在OpenGL中可以使用函數
- GLAPI void GLAPIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
來進行對窗口的映射,如圖13。
圖13 視口變換glViewport(width/2, 0, width/2, height/2)
舉個例子說明,比如上個階段中得到了一個頂點的坐標為(0,0,0.5,1),根據這個坐標,該頂點位于投影平面的正中間。如果將該點映射到大小為 50*50的窗口上時,那么它應該位于屏幕的中間,坐標為(25,25, 0.5,1)。當然這里深度值0.5是不會改變的。有的同學肯定有疑問了,既然投影到了窗口上,那么還要深度值0.5干什么?這里要注意的是,雖然在窗口 上顯示時只需要x,y坐標就夠了,但是要在2D窗口上顯示3D圖形時深度值是不可少的。這里的深度值不是用于顯示,而是用于在光柵化的時候進行深度測試。
OpenGL也會根據glViewport函數提供的參數值生成一個視口變換矩陣
該矩陣把上個階段得到的正規化坐標映射到窗口上,并且將正規化坐標中的深度值在轉換到0到1之間。所以在深度緩沖中最大值為1,最小值為0。視口變換結束 后,OpenGL中主要的圖形管線階段就算完成了,后面就是光柵化等等操作。再來回顧一下圖1,現在相信大家對這個渲染管線有了一定的認識了,也明白了每 一個階段對應的變換矩陣以及如何進行坐標之間的轉換的。
2. 屏幕坐標轉換為世界坐標
通過前面的教程,以及現在大家對OpenGL整個渲染管線理解后,現在要將屏幕上一點坐標轉換為世界坐標就比較容易了。從圖形管線的開始到結束,一 個模型坐標系中的坐標被轉化為了屏幕坐標,那么現在把整個過程倒過來的話,屏幕上一點坐標也可以轉為為世界坐標。只要在對應的階段求得對應變換矩陣的逆矩 陣,就可以得到前一個階段的坐標。這整個過程可以用圖14表示。
圖14屏幕坐標轉換為世界坐標
圖中顯示的過程完全就是OpenGL渲染管線的逆過程,通過這個過程,屏幕上的點就可以轉化為世界坐標系中的點了。可能又有的同學要問,當鼠標點擊屏幕上一點的時候并沒有深度信息,轉換的時候要怎么辦呢?這個時候可以使用OpenGL函數
- void glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
該函數能夠獲得屏幕上一點對應像素的深度信息。有了這個深度信息,就可以利用上面過程把屏幕上一點轉換為世界坐標了。在OpenGL中,上面的過程其實已經有現成的函數可以使用,那就是
- int APIENTRY gluUnProject (
- GLdouble winx, GLdouble winy,
- GLdouble winz,
- const GLdouble modelMatrix[16],
- const GLdouble projMatrix[16],
- const GLint viewport[4],
- GLdouble *objx, GLdouble *objy,
- GLdouble *objz);
該函數直接將屏幕上一點轉換對應的世界坐標,該函數的內部實現其實還是上面的那么逆過程。下面給出利用該函數獲取世界坐標的代碼段。
- GVector screen2world(int x, int y)
- {
- GLint viewport[4];
- GLdouble modelview[16];
- GLdouble projection[16];
- GLfloat winX, winY, winZ;
- GLdouble posX, posY, posZ;
- glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
- glGetDoublev(GL_PROJECTION_MATRIX, projection);
- glGetIntegerv(GL_VIEWPORT, viewport);
- winX = (float)x;
- winY = (float)viewport[3] - (float)y;
- glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
- gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
- GVector v(4, posX, posY, posZ, 1.0);
- return v;
- }
代碼中函數返回類型GVector是用戶定義的向量類,返回的是齊次坐標。
Linux下對于新添加的用戶,用戶密碼過期時間是從/etc/login.defs中PASS_MAX_DAYS提取的,普通系統默認就是99999,而有些安全操作系統是90。更改此處,只是讓新建的用戶默認密碼過期時間變化,已有用戶密碼過期時間仍然不變。
[root@linuxidc ~]# chage --help
Usage: chage [options] user
Options:
-d, --lastday LAST_DAY set last password change to LAST_DAY
-E, --expiredate EXPIRE_DATE set account expiration date to EXPIRE_DATE
-h, --help display this help message and exit
-I, --inactive INACTIVE set password inactive after expiration
to INACTIVE
-l, --list show account aging information
-m, --mindays MIN_DAYS set minimum number of days before password
change to MIN_DAYS
-M, --maxdays MAX_DAYS set maximim number of days before password
change to MAX_DAYS
-W, --warndays WARN_DAYS set expiration warning days to WARN_DAYS
chage:密碼失效是通過此命令來管理的。
參數意思:
-m 密碼可更改的最小天數。為零時代表任何時候都可以更改密碼。
-M 密碼保持有效的最大天數。www.linuxidc.com
-W 用戶密碼到期前,提前收到警告信息的天數。
-E 帳號到期的日期。過了這天,此帳號將不可用。
-d 上一次更改的日期
-i 停滯時期。如果一個密碼已過期這些天,那么此帳號將不可用。
-l 例出當前的設置。由非特權用戶來確定他們的密碼或帳號何時過期。
[root@linuxidc ~]# chage -l root
Last password change : Oct 19, 2010
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
更改用: chage -M 90 root
[root@linuxidc ~]#chage -M 90 root
[root@linuxidc ~]#chage -l root
如果以后添加一個用戶,那么默認的時間還是沒改的,還必須得去/etc/login.defs修改PASS_MAX_DAYS 的默認值.那么如果我直接修改全局/etc/login.defs所在的用戶會跟著改變嗎? 據我的測試是不會改變的,除非重啟后,但我們的服務器不是你想重啟的就可以重啟的!如果管理嚴格的地方,重啟還得經過很多程序步驟.改完全局時,沒有更改 的用戶,想要讓他也同樣具備此功能.就得一個個的執行!
你也可以直接用vim 編輯器去編輯PASS_MAX_DAYS 99999
也可以用其它的工具
[root@linuxidc ~]#sed -i.bak -e 's/^\(PASS_MAX_DAYS\).*/\1 90/' /etc/login.defs
查看一下是否
[root@linuxidc ~]#cat /etc/login.defs |grep "PASS_M";
強制用戶登陸時修改口令
[root@linuxidc ~]#chage -d 0 username(linux)
[root@linuxidc ~]#passwd -f username(solaris)
強制用戶下次登陸時修改密碼,并且設置密碼最低有效期0和最高有限期90,提前15天發警報提示
[root@linuxidc ~]#chage -d 0 -m 0 -M 90 -W 15 root(linux)
[root@linuxidc ~]#passwd -f -n 0 -x 90 -w 15 root(solaris)
查看某個用戶的密碼設置情況
[root@linuxidc ~]#chage -l username
修改密碼配置文件
[root@linuxidc ~]#vi /etc/login.defs
Linux ssh建立一個新的用戶包括兩個步驟,第一步是使用useradd命令完成一個新用戶的初始化設置工作;第二步是用passwd為這個新用戶設置密碼。例如,我們要給系統添加一個用戶叫floatboat ,密碼為lan2010,那相關的操作是:
useradd floatboat -d /u01/Lanlp<回車>
這時候系統沒有任何顯示。接著:
passwd floatboat <回車>
系統顯示:
Changing password for user floatboat
New UNIX password:
你輸入:
lan2010<回車>
注意,由于linux并不采用類似windows的密碼回顯(顯示為*號)——為避免你輸入密碼時被人注意到有多少位——所以,輸入的這些字符你是看不見的。
系統顯示:
Retype new UNIX password:
你再重新輸入一次密碼,然后回車確認,這時系統會顯示:
passwd:all authentication tokens updated successfully
表示你修改密碼成功了。
到這里,新用戶的創建工作就算完成了。
下面,我們再補充一些有關增加新用戶的常識:
1、useradd所做的初始化操作已經包括在/home目錄下為floatboat帳號建立一個名為floatboat的主目錄。如果你不想 使用這個缺省的目錄,而希望把他的主目錄放在/home/goal里(還放在/home下,只是一種良好的習慣,沒有其他什么特別的要求),可以使用 useradd的參數-d,命令如下:
useradd -d /home/goal floatboat
2、useradd的初始化操作還包括為用戶單獨建立一個與用戶名同名的組(floatboat組)。這叫用戶私有組的機制,與默認組機制相對 應。對用戶分組一是方便管理,二是可以明確權限。復雜的我們將在以后的深入內容中探討。我們如果想讓此用戶加入一個已有的組的話,可以使用-g參數。例如 我們想讓floatboat加入webusers組,那么可以使用以下命令:
useradd -g webusers floatboat
同樣的,我們還可以使用-G參數使他同時加入多個組,例如webusers和ftpusers:
useradd -G ftpusers,webusers floatboat
3、passwd命令為一個用戶設置密碼,但它實質上是一個修改密碼的程序。只有超級用戶和用戶自己可以修改密碼,其它的普通用戶沒有給他修改 密碼的 權利。用戶密碼的組成要盡量的復雜,最好包括字母、數字和特殊符號,而且最好設成6位以上。太短passwd程序不允許,只是單純的字母或單純的數 字,passwd也會有意見。你都會看見passwd出現的提示的,不要害怕,仔細看看到底它是怎么說的:)
4、你在增加一個新用戶的時候,也可以設置用戶登錄的shell。缺省的,系統提供了/bin/bash。你如果非要指定的話,可以使用-s參數就可以了。例如
useradd -d /www -s /usr/bin/passwd floatboat
注意,這些參數是可以一塊使用的,如上例所示,它表示增加新用戶,并把其主目錄路徑設置在/www,登錄的shell為/usr/bin/passwd。關于shell的更詳細的說明,請參考下面的修改用戶的個人設置相關內容。
5、刪除一個用戶可以使用userdel命令,直接帶用戶名做參數就可以了。
修改用戶的設置
對現有用戶的修改,比較常用的主要是修改密碼(使用passwd就好了),修改用戶的登錄shell,修改用戶所屬的默認組,設置帳號有效期,修改用戶的說明信息等等,偶爾也會用到修改用戶主目錄。
修改用戶的登錄shell
使用chsh命令可以修改自己的shell,只有超級用戶才能用chsh username為其它用戶修改shell設置。注意,指定的shell必須是列入/etc/shells文件中的shell,否則該用戶將不能登陸。
一般,比較常見的shells文件包括下面這些shell:
/bin/bash2
/bin/bash
/bin/sh
/bin/ash
/bin/bsh
/bin/tcsh
/bin/csh
而網管們還喜歡在里面加上/usr/bin/passwd,這是為了不然用戶通過控制臺或telnet登錄系統,卻可以使用修改帳戶密碼(比如在FTP里 用)。以及/bin/false,也就是不讓這個用戶登錄的意思嘍^&^,連FTP也不能用。
你也可以使用usermod命令修改shell信息,如下所示:
usermod -s /bin/bash floatboat
其中/bin/bash和floatboat應取相應的shell路徑文件名及用戶名。
還有一種情況,就是你為用戶設置了一個空的shell(就是""),也就是說,這個用戶沒有shell。呵呵,絕對沒有在我還未曾見過,因為這種用戶登錄后,系統還是會給它一個shell用的。不信你試試:
usermod -s "" floatboat
這種用戶根據系統的不同,會有一個sh或bash進行操作,我也沒有看出功能上和其它普通用戶登錄有什么不同。
修改用戶所屬的默認組
這個功能也可以通過usermod命令來實現,使用-g參數,例如把floatboat的默認組改為nobody,可以使用如下命令:
usermod -g nobody floatboat
nobody在類UNIX系統中一般都意味著沒有任何權限。
設置帳號有效期
如果使用了影子口令,則可以使用如下命令來修改一個帳號的有效期:
usermod -e MM/DD/YY username
例如把用戶floatboat的有效期定為2001年12月31日:
usermod -e 12/31/01 floatboat
如果把該用戶的有效期設為已經過去的時間,就可以暫時禁止該用戶登錄系統。
修改用戶的說明信息
修改用戶的說明信息,最簡單的方法莫過于直接修改/etc/passwd文件,找到對應的用戶記錄行,例如下列行:
floatboat:x:503:503::/home/floatboat:/bin/bash
你可以直接在第四個冒號和第五個冒號之間插入該用戶的說明就可以了。其實,很多用戶設置都可以在這修改,比如該行最后一部分/bin/bash就是用戶登錄shell的設置。關于這個/etc/passwd文件,我們后面將進一步的深入探討。
修改用戶主目錄
修改用戶的主目錄主要使用usermod命令的-d參數,例如:
usermod -d /www floatboat
這一行將floatboat的主目錄改到/www。如果想將現有主目錄的主要內容轉移到新的目錄,應該使用-m開關,如下所示:
usermod -d -m /www floatboat
文件目錄的權限
linux下,每一個文件、每一個目錄都有一個屬主,并針對用戶自己、用戶所在組、其它所有帳號(組)分別設定讀、寫、執行三種權限。例如,我(假定是webusers組的floatboat帳戶的擁有者)使用如下命令建立一個新的文件
touch mytestfile
然后我們使用ls -l mytestfile這一命令來查看這個文件的權限狀態(關于ls命令,可以查閱本站的命令查詢),可以得到如下的屏幕輸出顯示:
-rw-rw-r-- 1 floatboat webusers 0 Feb 6 21:37 mytestfile
輸出由空格分為9個部分,我們比較關心第一、三、四個字段,分別表示文件權限屬性、文件所有者帳戶、文件所屬組。
◆使用chown命令修改文件的主人
當你新建立一個文件的時候,文件的所有者當然就是你了。這一事實只有超級用戶(比如說root)才可以通過chown命令改變(例如chown otheruser mytestfile,把mytestfile文件的屬主改為otheruser)。普通用戶不能把自己的文件“送”給別人,不然你把有特殊目的的程序給 了root怎么辦?:)
chown命令的用法比較簡單。這里我先假設你現在擁有超級用戶權限,那么你就可以使用如下命令將一個文件“送給”floatboat了:
chown floatboat /home/floatboat/thefileisrootcreate.txt(假定該文件是由root創建的)
修改一個目錄的所有者也是類似的:
chown floatboat /home/newboat
當然,如果這個目錄還有子目錄及文件需要同時送給floatboat,chown也是支持-R參數的:
chown -R floatboat /home/newboat
如果你同時想修改文件/目錄所屬的組的話,你可以使用以下命令方便的達到目的:
chown -R floatboat.ftpusers /home/newboat
這樣,不但文件主人得到了修改,文件所屬的組也變成了ftpusers
◆修改文件的組屬性
文件所屬組你倒是可以改變,前提是:
1、你的超級用戶。
2、你同時屬于兩個或兩個以上的組。
兩個條件你至少具備一個,你才能夠把文件所屬舊組變為新組。使用如下的命令將當前目錄下所有html文件所屬的組改為httpd:
chgrp httpd *.html
和chown命令一樣,chgrp也可以使用-R參數對一個目錄內的所有文件和子目錄進行遞歸的修改組屬性。
<提示>:你可以使用不帶參數的groups命令查看自己屬于哪個組。
文件權限的設定是我們這一小節討論的核心,我們主要介紹chmod命令的兩種用法。
◆使用訪問字符串設置文件目錄權限 正如前面所說的,每一個文件、目錄都針對用戶自己、用戶所在組、其它所有帳號(組)分別有讀、寫、執行三種權限及其組合。當一個普通用戶新建一 個文件 的時候,它默認的訪問權限顯示就如我們剛才所舉例子的第一個字段所示。總共十位字符“-rw-rw-r--”,第一位是目錄區分標志,如果是d的話,表示 這是一個目錄。第二到四位分別表示文件所有者的讀(r:read)、寫(w:write)、執行(x:execute)屬性,第五到七位是文件所屬組的 讀、寫、執行權限,第八到第十位則是其它用戶的讀、寫、執行權限。如果對應的位是相應的字母,就是有這相應權限,否則為“-”,表示沒有獲得這個許可。象 剛才例子中的文件就是自己可讀寫,本組可讀寫,其它用戶可讀,所有的用戶(包括自己)都不能執行它。
我們的用u、g、o分別來指代用戶(user)、組(group)、其它帳戶(other),就可以方便的設置文件和目錄的權限了。當然,我們也可以用a來表示所有的這三項。
例如,我們要對所有perl的腳本文件設定權限,對所有用戶都可以讀和執行,文件所有者還允許寫許可,那么我們可以使用如下命令:
chmod a+rx,u+w *.pl
注意:如果要使用多個訪問字符串,它們之間要用逗號隔開,各個許可字符串之間不允許有空格。正如上例所示。
如果要修改目錄中所有文件和子目錄的權限屬性,可以使用chmod提供的-R參數來遞歸修改。例如,下列命令將/www/site1目錄及其下面的子目錄的權限屬性設定為所有者和組可讀、寫、執行,其它用戶不可訪問:
chmod -R a+rwx,o-rwd /www/site1
注意,不要輕易使用-R選項,這可能會帶來安全隱患。
使用字符串方便了理解,單輸入那么多字母還是有點累,如果你對8進制有些概念的話,可以使用下面介紹的方法來做權限設置。
◆使用八進制數設置文件目錄權限
我們知道,在ls -l的輸出中,文件權限表示為“-rw-rw-r--”,前一位只和是否為目錄有關,其它九位正好可以分成三段,每段三位,“rw-”、“rw-”和 “r--”,“-”代表無效“0”,其它字符代表有效“1”,那么這個文件的權限就是“110”、“110”、“100”,把這個2進制串轉換成對應的8 進制數就是6、6、4,也就是說該文件的權限為664(三位八進制數)。我們也可以使用類似這種三位八進制數來設定文件授權,如上邊兩個例子,就也可以寫 為:
chmod 755 *.pl
chmod -R 770 /www/site1
是不是很簡潔?關鍵在于你能根據你需要設定的權限正確的選擇八進制數(利用八進制數的二進制表示可以非常輕易的做到這一點)。
◆讀、寫、執行的權限說明
1、所謂寫的權限,也就是對文件修改和刪除的權限。如果目錄的寫權限也對你開放了,則可以創建、刪除或修改該目錄下的任何文件或自目錄——即使該文件和子目錄并不屬于你。
2、對目錄有只讀許可的用戶,不能用cd命令進入該目錄;還必須同時有執行許可才可以進入該目錄。
3、必須同時擁有讀和執行權限才可以使用ls這樣的程序列出目錄內容清單。
4、只對目錄有執行權限的用戶,想訪問該目錄下的文件有讀權限的文件,必須知道該文件名才可以訪問。
兩個重要文件:passwd與group
在linux的安全機制里,/etc/passwd與/etc/group這兩個文件占著非常重要的地位。它們控制著linux的用戶和組一些重要設置。
◆/etc/passwd文件說明
下面是一個RHlinux里普通的passwd文件的例子:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
……
operator:x:11:0perator:/root:
games:x:12:100:games:/usr/games:
gopher:x:13:30:gopher:/usr/lib/gopher-data:
ftp:x:14:50:FTP User:/home/ftp:
nobody:x:99:99:Nobody:/:
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
named:x:25:25:Named:/var/named:/bin/false
postgres:x:26:26ostgreSQL Server:/var/lib/pgsql:/bin/bash
lanf:x:500:500::/home/hujm:/bin/bash
mysql:x:101:101:MySQL server:/var/lib/mysql:/bin/bash
imnotroot:x:0:0::/home/imnotroot:/bin/bash
在這個文件里只有一個普通帳號lanf。其它都是系統或系統服務的進程需要的帳號,包括我們非常熟悉的root這個超級用戶。在passwd的文件里,每一行被冒號(":")分成7個部分,分別是:
[用戶名]:[密碼]:[UID]:[GID]:[身份描述]:[主目錄]:[登錄shell]
其中:
⒈[用戶名]是passwd文件里各記錄行唯一的有"唯一性"要求的域。也就是說每一行的第一個區域的內容都不能相同,其它區域就無所謂了。
⒉[密碼]區域在以前,保存著一個經過不可逆的哈希算法進行DES加密的13位字符,但不包括單引號和冒號。這13位字符中,前兩位是密鑰,在 加密的 時候隨機生成的。由于這個字符串不包括單引號,所以以前有一種不修改密碼又禁止用戶登錄的方式就是在密碼前面加一個單引號。值得注意的是,現在由于使用了 shadow口令,在密碼區域只有一個x字符。
⒊[UID]雖然是系統用來標志文件歸屬,確定各種權限的標志,但這個區域的內容并不要求唯一的。比較常見而又與安全問題相關的一個例子是有多 個 UID和GID均為0的用戶帳號。注意到在該文件最后一行還有一個UID和GID為0的用戶imnotroot,雖然它聲稱自己不是root,但是它卻有 和root完全相同的權限,因為系統并非根據[用戶名],而是根據UID和GID來分用戶的權力的。所以,這種情況無疑為系統埋下了安全的zhadan。 但是,當 imnorroot做鎖定屏幕等操作的時候,如果它的密碼和root的不一樣,它將無法解鎖,因為系統只是查到第一個UID為0的用戶(自然是root) 后,就不在往下查找了——它當UID也是唯一的。
⒋[GID]用戶默認的組ID,這個ID可以在文件/etc/group里查到對應的組名。
⒌[身份描述]:就是用戶的身份說明,默認的是無任何說明,可人工添加。
⒍[主目錄]:用戶的主目錄,可以使用前面介紹的命令修改。
⒎[登錄shell]:用戶登錄時系統提供的shell,請參考前面的有關內容。
<注意>:[UID]和[GID]小于500的一般都是系統自己保留,不做普通用戶和組的標識的,所以新增加的用戶和組一般都是UID和GID大于500的。
◆/etc/group文件說明
下面是RH的一個group文件的例子:
root:x:0:root,hujm,hjm
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
sys:x:3:root,bin,adm
adm:x:4:root,adm,daemon
tty:x:5:
disk:x:6:root
lp:x:7:daemon,lp
mem:x:8:
kmem:x:9:
wheel:x:10:root
mail:x:12:mail
news:x:13:news
uucp:x:14:uucp
……
hujm:x:503:root,mynoshell,hjm
mysql:x:101:
mynoshell:x:505:
ftpusers:x:506:
它總共分四個部分:
[組名]:[密碼域]:[GID]:[組員列表]
意思非常明顯,需要說明一下的是,由于組一般都不用密碼保護,所以雖然看起來密碼域有個X字符,其實那只表示使用了SHADOW(對應文件為 gshadow)。組員列表用逗號分隔各個帳號。另外,一個組的組員如果默認登錄組就是它的話,那么在組員列表里將不顯示這個組員的帳號,例如用如下命令 增加的用戶:
useradd -g ftpusers floatboat
在/etc/group文件里ftpusers的組員列表將不顯示這個組員(真是失敗),而只是在passwd文件中的GID被設置為506。而使用如下命令:
usermod -G ftpusers,mysql,webusers floatboat
就可以看見相關的組后邊加上了floatboat帳號。當然,你可以直接用vi來直接編輯這個文件。
group文件和passwd文件是通過GID聯系在一起的,這有點象關系數據庫。根據passwd文件中一個帳戶的GID,可以在group 文件中 找到對應的組名。如果采用了用戶私有組機制的話,那么一般新增一個帳號,就會有對應的一個與帳號同名的組增加到group文件中。雖然這時passwd文 件中具有唯一性的[用戶名]字段和group文件中具有唯一性的[組名]字段一樣,并不代表著它們是通過這兩個字段形成一一對應的關系的。千萬別忘記,系 統對數字(UID,GID)更加敏感.
轉自:http://blog.csdn.net/yetyongjin/article/details/7476860
這兩天做asterisk的性能測試,經常碰到這樣的錯誤:“Too many open files”。 我們知道,Linux下,文件描述符就是一個簡單的整數值,習慣上,標準輸入(standard input)的文件描述符是 0,標準輸出(standard output)是 1,標準錯誤(standard error)是 2。POSIX 定義了STDIN_FILENO、STDOUT_FILENO 和STDERR_FILENO 來代替 0、1、2。這三個符號常量的定義位于頭文件 unistd.h。
文件描述符的有效范圍是0 到OPEN_MAX。那么一個進程最多到底能打開多少個文件描述符呢?下面,就以asterisk進程為例來說明。
#ps –ef | grep asterisk

從第二列得到asterisk的PID為19488。
# cat /proc/19488/limits

紅線一行說明asterisk進程最大能打開1024個文件描述符(不包含它的子進程或創建出來的線程)。在/proc/19488/task/目錄下,詳細列出了其下的子任務的情況,每個子文件夾里同樣有一個limits文件,限定了各子任務的情況。
一個進程打開了幾個文件描述符呢?
# ll /proc/19488/fd/

子目錄fd中,詳細列出了進程打開的每個文件描述符,同樣,/proc/19488/task/XXXX/fd下也會有子任務打開的文件描述符的情況。要知道有幾個,執行
# ll /proc/19488/fd/ | wc -l
怎樣知道一個進程及其子進程和哪些文件有關聯呢?lsof可以完成這工作。請注意,關聯文件和打開文件描述符是兩個不同的概念,關聯文件的數量可能遠遠大于打開的文件描述符的數量。
# lsof | grep asterisk | wc –l
也可以用父進程的PID過濾
# lsof | grep 19488 | wc –l
我這里得到的值是9525
怎樣修改文件描述符的限定呢?臨時修改,可以通過ulimit。
# ulimit -SHn 2048
但是這樣只能影響到當前的session,當終端重新連接或當前用戶退出,配置就失效了。如果想永久變更需要編輯/etc/security/limits.conf 文件,添加如下兩行:
* hard nofile 2048
* soft nofile 2048
內核參數對文件描述符也有限制,如果設置的值大于內核的限制,也是不行的:
查找file-max的內核參數:
# sysctl -a|grep file-max
更改file-max的內核參數:
# sysctl -w file-max=65535
Sysctl也是臨時的,要想永久生效,可以通過更改sysctl的文件,編輯/etc/sysctl.conf文件,添加或修改以下一行:
fs.file-max=65535
需要注意的是,文件描述符的限制,不局限于這里描述的這些,還可能和進程的啟動參數、用戶的環境設置有關。當然,如果是進程BUG造成文件描述符沒有及時關閉回收,這增大限制也只是治標,根本上還得修復BUG。
此外,lsof會列出系統中所占用的資源,但是這些資源不一定會占用打開的文件描述符(比如共享內存,信號量,消息隊列,內存映射.等,雖然占用了這些資源,但不占用打開文件號),因此有可能出現cat /proc/sys/fs/file-max 的值小于lsof | wc -l。asterisk本身提供了一個啟動腳本,名為safe_asterisk,腳本里面就對文件描述符做了一些設置。