|
OS/리눅스 & 유닉스 2012. 2. 1. 20:47
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void siguser_handler(int signo)
int main(int argc, char *argv[])
{
struct sigaction act_new;
act_new.sa_handler = siguser_handler;
sigfillset(&act_new.sa_mask);
//sigaddset
sigdelset(&act_new.sa_mask, SIGUSR1);
sigprocmask(SIG_SETMASK,&act_new.sa_mask,NULL);
sigaction(SIGUSR1,&act_new,NULL);
while(1)
{
sleep(1);
}
return 0;
}
void siguser_handler(int signo)
{
return 0;
}
OS/리눅스 & 유닉스 2012. 2. 1. 20:44
쓰레드 시그널 관련 함수들
1.1. 사용법
#include <pthread.h>
#include <signal.h>
int pthread_kill(pthread_t thread, int signo);
int pthread_sigmask(int how, const sigset_t *newmask,
sigset_t *oldmask);
int sigwait(const sigset_t *set, int *sig);
|
1.2. 설명이 함수는 현재 쓰레드에 시그널newmask와 how 를 이용해서 시그널 마스크를 만든다. how는 SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK중 하나를 선택할 수 있다. SIG_BLOCK는 현재 설정된 시그널 마스크에 newmask를 추가하며 SIG_UNBLOCK는 현재 설정된 시그널 마스크에서 newmask를 제거하고 SIG_SETMASK는 newmask로 현재 시그널 마스크를 설정한다
pthread_kill()은 시그널번호signo를 가지는 시그널을 스레드 식별번호thread를 가지는 쓰레드에 전달한다.
sigwait()는 시그널 셋set에 지정된 시그널이 전달될 때까지 해당영역에서 대기한다. 만약 시그널을 받게 되면 받은 시그널 번호를 sig에 복사하고 리턴한다. set에 명시된 시그널들은 반드시 봉쇄로 지정되어야 한다. 그래야만 sigwait()에 의해서 검출되고 리턴된다. 만약 해당 시그널에 대해서 시그널 핸들러가 지정되어 있다면 함수는 호출되지 않게 된다.
1.3. 반환값성공하면 0을, 실패하면 0이 아닌 값을 리턴한다.
1.4. 에러
- ESRCH
th식별자를 가진 쓰레드가 존재하지 않는다.
- EINVAL
th식별자를 가진 쓰레드가 이미 detach상태에 있다.
1.5. 예제
#include <pthread.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/*
* 시그널 핸들러
* 핸들러가 호출된 쓰레드의 ID와 시그널 번호를 출력한다.
*/
void sig_handler(int signo)
{
printf("SIGNAL RECV TH ID %d : %d\n", pthread_self(),signo);
}
void *threadfunc2(void *arg);
void *threadfunc(void *arg);
void *s_signal(void *arg);
// 쓰레드 ID를 저장한다.
int sigid[2];
int main()
{
int n, i, j;
pthread_t threadid;
// 원하는 쓰레드로 시그널이 전달하는지 확인하기 위해서
// 쓰레드 ID를 확인한다.
if ((n = pthread_create(&threadid, NULL, threadfunc2, NULL)) != 0 )
{
perror("Thread create error ");
exit(0);
}
sigid[0] = threadid;
printf("thread2 id %d\n", threadid);
if ((n = pthread_create(&threadid, NULL, threadfunc, NULL)) != 0 )
{
perror("Thread create error ");
exit(0);
}
sigid[1] = threadid;
printf("thread id %d\n", threadid);
if ((n = pthread_create(&threadid, NULL, s_signal, NULL)) != 0 )
{
perror("Thread create error ");
exit(0);
}
pthread_join(threadid, NULL);
}
void *threadfunc(void *arg)
{
int i=0, j;
struct sigaction act;
sigset_t newmask;
// 결과의 확인을 위해서 쓰레드 ID를 출력한다.
printf("SIGINT Thread Start (%d)\n", pthread_self());
// SIGINT에 대해서 시그널 핸들러를 등록했지만
// SIGINT를 블럭하도록 했으므로 시그널은 전달되지 않는다.
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
act.sa_handler = sig_handler;
sigaction(SIGINT, &act, NULL);
pthread_sigmask(SIG_BLOCK, &newmask, NULL);
while(1)
{
printf("%d\n", i);
i++;
sleep(1);
}
return ;
}
void *threadfunc2(void *arg)
{
struct sigaction act;
int lsigno;
sigset_t newmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
// 시그널 핸들러를 등록 시켰지만
// sigwiat()가 가디리고 있으므로 실행되지 않는다.
act.sa_handler = sig_handler;
sigaction(SIGINT, &act, NULL);
while(1)
{
sigwait(&newmask, &lsigno);
printf("i receive signo %d\n", lsigno);
sleep(1);
}
}
/*
* SIGINT를 두개의 쓰레드로 서로다른 시간간격으로
* 전달한다.
*/
void *s_signal(void *arg)
{
int i = 1;
while(1)
{
sleep(1);
i++;
if((i % 7) == 0)
{
printf("Send SIGINT %d\n", sigid[0]);
pthread_kill(sigid[0], SIGINT);
}
if((i % 11) == 0)
{
printf("Send SIGINT %d\n", sigid[1]);
pthread_kill(sigid[1], SIGINT);
}
}
}
|
OS/리눅스 & 유닉스 2012. 2. 1. 19:42
1. 시그널의 개념
1)프로세스에 사건의 발생을 알려주는 소프트웨어적인 통지
2)하드웨어에 의해 발생되는 시그널 : 0으로 나누거나 잘못된 메모리 참조
3)비동기적 사건들의 발생을 프로세스에게 알려줌(인터럽트)
*비동기적 : 시그널을 보낸 쪽에서 받는 쪽의 응답을 기다리지 않음
*인터럽트 : 수행하고 있는 프로세스 중단(일시 중단)
4)시그널 처리 단계
ⓐ생성 - 프로세스가 수신해야 하는 이벤트가 일어남
ⓑ처리 - 프로세스가 시그널을 인지하고 그것에 적합한 동작을 수행함
*이 두 단계 사이에 위치할 때 시그널이 펜딩(지연) 되었다고 함
5)시그널 함수의 원형
#include (*signal(int sig, void(*func)(int)))(int);
*sig는 신호 번호, func는 신호처리 핸들러 함수이다.
이 함수는 신호를 처리하기 위해 이전에 설정되어 있던 핸들러 함수를 변환 하거나 SIG_INT또는 SIG_DFL를 반환한다.
SIG_IGN은 처리를 무시하라는 값이며 SIG_DEF는 기본 동작을 복구하라는 값
2. 유닉스 표준 시그널의 종류(POSIX 표준)
*SIGABRT - 프로세스 중단
*SIALARM - 알람 알림(비정상 종료)
*SIGBUS - 정의되지 않은 메모리 영역 접근
*SIGCHLD - 자식 프로세스의 종료, 중단 , 계속
*SIGCONT - 중단되었다면 재시작합낟.
*SIGFPE - 0으로 나누는 것과 같은 산술연산 에러
*SIGHUP - 제어 터미널(프로세스)의 중단(종료)
*SIGILL - 잘못된 하드웨어 명령어
*SIGINT - 사용자 인터럽트를 커널에 의하여 단말기와 연결된 프로세스에 보내짐(ctrl+c)
*SIGKILL - 종료(어떤 경우에도 프로세스 종료가 보장되는 신호) : 슈퍼유저가 통제하는 방법
*SIGPIPE - 읽는 프로세스가 없는 상황에서의 PIPE에 대한 쓰기 발생시
*SIGQUIT - 대화형 종료 : 코어 덤프(ctrl+i)
코어 덤프 : 종료 시점전까지의 데이터를 저장한 파일
*SIGSEGV - 잘못된 메모리 접근
*SIGSTOP - 실행 중지(처리되거나 무시될 수 없음)
*SIGTERM - 프로세스 종료 시그널 (kill 시그널 전송 전에 전송됨)
*SIGSTOP-터미널 중지
*SIGTTIN-백그라운드 프로세스가 읽기를 시도
*SIGTTOU - 백그라운드 프로세스가 쓰기를 시도
*SIGURG - 소켓에 높은 대여폭 데이터가 유용함
*SIGUSR1 - 유저 정의 시그널1
*SIGUSR2 - 유저 정의 시그널2
3. 시그널을 다루기 위한 시스템 호출/표준 라이브러리 함수
1)시그널 집합 생성
함수 원형
#incldue <stdio.h>
int sigemptyset(sigset_t *set); : 시그널이 없는 비어있는 상태로 초기화
int sigfillset(sigset_t *set); : 모든 시그널이 포함된 상태로 초기화
int sigaddset(sigset_t *set); : 시그널 추가
int sigdelset(sigset_t *set); : 시그널 삭제
int sigismember(const sigset_t *set, int signo) : 시그널 찾기
|
ⓐset - sigset_t형의 시그널 집합
*시그널 각각을 인수로 하면 동일한 작업을 수십번 해야 한다.
ⓑsignum - 시그널 번호
ⓒ반환값 : 호출이 성공하면 0, 실패하면 -1반환 sigisnumber만 성공하면 1이나 0을 반환하고 실패하면 -1을 반환한다.
ex)예제 프로그램
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void main()
{
sigset_t set; //시그널 집합 선언
sigemptyset(&set);
result = sigisismember(&set, SIGALARM); //SIGALARM이 등록 결과 저장
printf("SIGALARM is %s a member\n", result ? "" : "not"); //삼항 연산자 사용 비교
sigaddset(&set, SIGALARM); //시그널 추가
result = sigisismember(&set, SIGALARM);
printf("SIGALARM is %s a member\n", result ? "" : "not");
sigfillset(&set); //모든 시그널이 포함된 상태로 초기화
result = sigismember(&set, SICHLD);
printf("SIGCHLD is %s a member\n", result ? "" : "not");
sigadelset(&set, SIGCHLD); //삭제
result = sigismember(&SET, SIGCHLD);
printf("SIGCHLD is %s a member\n", result ? "" : "not");
}
2)Sigaction
특정 시그널을 받았을 때 프로세스가 취해야 할 행동 지정
-signum으로 지정한 시그널에 대해서 act로 지정한 행동을 취한다.
-새로운 행동인 act가 등록되면서 기존의 행동은 oldact에 저장된다.
-원형
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum-시그널 번호
act-프로세스가 지정한 시그널에 대한 행동 저장
oldact-이전에 지정되어 있는 시그널 행동 저장, 보통 NULL
반환값 : 호출 성공시 0 실패시 -1
|
struct sigaction {
void (*sa_handler)(int); /*SIG_DFL, SIG_IGN*/ =주소값
void (*sa_sigaction)(int, siginfo_t*, void*); /*실시간 핸들러*/
sigset_t sa_mask; /*핸들러 실행 동안 추가 블록될 시그널*/
int sa_flag; /*옵션 값*/
}
ex)예제
①#include <signal.h>
②#include <unistd.h>
③#include <stdio.h>
④int num=0;
⑤void main()
{
⑥static struct sigaction act; //구조체 선언
⑦void int_handle(int);
⑧act.sa_handler = int_handle;
⑨sigfillset(&(act.sa_mask));
⑩sigaction(SIGINT, &act, NULL); //SIGINT 등록
⑪while(1) {
⑫printf("i'm sleepy..\n");
⑬sleep(1);
⑭if(num >= 3) {
⑮ act.sa_handler = SIG_DFL;
16.sigaction(SIGINT, &act, NULL);
}
}
}
void int_handle(int signum)
{
17.printf("SIGINT : %d\n", signum);
printf("int_handle called %d times\n", ++num);
}
*i'm sleepy를 계속 출력하다가 사용자가 ctrl+c를 입력하면 인터럽트 메세지를 출력하고 4번 출력시 종료
원리 : 1. 8번에서 act.sa_handler에 int_handle의 주소 저장
2. 9번에서 블록될 시그널의 목록을 저장한 상태로 초기화
3. 10번에서 sigaction의 act에 SIGINT를 받았을 때의 행동 저장.
4. 11번 수행을 하다가 SIGINT(ctrl+c)를 입력받으면 SIGINT의 signum(2)를 입력받는 int_handle함수로 이동
5. 받을 때마다 17번째 줄을 수행(3번 입력 전까지)
6. 3번 입력 받을시 14번으로 이동하고, act에 SIG_DFL저장
7. SIG_DFL이 SIG_INT이므로 SIGINT가 들어오면 프로그램 종료
3)sigprocmask
- 시그널 집합 단위로 봉쇄될 시그널들의 목록을 설정
- 시스템이 대단히 중요한 코드를 실행 중이면 시그널을 무시하고 작업 종료 후 시그널 봉쇄 해제
- 원형
int sigprocmask(int how, const sigset_t *set, sigset_t *oldest);
how-행동 방식 결정
set-새롭게 적용하는 시그널 마스크
oldest-이전에 적용되어 있는 시그널 마스크 저장
반환값-호출이 성공할 경우 1, 실패할시 -1반환
|
int how
SIG_BLOCK : 현재 봉쇄된 시그널의 목록에 두 번재 인자 SET에 포함된 시그널을 추가한다.
SIG_UNBLOCK : 현재 봉쇄된 시그널의 목록에 두 번째 인자 set에 포함된 시그널을 제외
SIG_SETMASK : 현재 봉쇄 설정된 시그널의 목록에 두 번째 인자 SET에 포함된 시그널 대체
ex)예제
#incldue <unistd.h>
#include <stdio.h>
#include <signal.h>
void main()
{
sigset_t set;
int count = 3;
sigemptyset(&set);
①sigaddset(&set, SIGINT);
②sigprocmask(SIG_BLOCK, &set, NULL);
while(count)
{
③printf("don't disturb me (%d)\n", count--);
sleep(1);
}
④sigprocmask(SIG_UNBLOCK, &set, NULL);
⑤printf("you did not disturb me!!\n");
}
*사용자의 인터럽트 SIGINT를 봉쇄시켜서 시스템작업(while문 안의 문장수행)을 방해하지 못하게 함
인터럽트가 안들어올시 2번 3번 출력후 4번 출력, 들어올시 2번 출력후 완료
원리) ①1번에서 SIGINT를 시그널 집합에 추가
②블록될 시그널 목록에 SIGINT추가
③count가 영이 될 때까지 3번 문장 출력
④사용자 인터럽트 없을시 SIGINT를 언블록하고 5번 문장 출력
⑤사용자 인터럽트가 들어올 시, 3번을 3번 출력후 UBBLOCK된 SIGINT수행(종료)
4) KILL과 RAISE
-KILL은 특정 프로세스나 프로세스 그룹에게 지정한 시그널을 전달하고, RAISE는 자기 자신에게 지정한 시그널을 전달한다.
#include <sys/types.h>
int kill(pid_t pid, int sig);
#include <signal.h>
int raise(int sig);
pig=프로세스 식별 번호
sig=시그널 번호
반환값=호출 성공시 0, 실패시 -1
|
pid값에 따른 의미
pid>0 : 프로세스의 식별 번호. 해당프로세스에게만 시그널 보냄
pid=0 : 자신과 같은 그룹에 있는 모든 프로세스에게 시그널 보냄
pid=-1 : 자신과 같은 그룹에 있는 모든 프로세스에게 시그널을 보내지만, 프로세스 식별 번호가 1인 프로세스 제외.
pid<-1 : 프로세스 그룹 식별 번호가 -pid인 모든 프로세스에게 시그널 보냄.
5) Alarm
-지정 시간 경과후 자신에게 SIGALARAM 시그널을 보낸다
*unsigned int alarm(unsigned int seconds);
second : 초 단위의 시간, 지정한 시간 중 남은 시간 반환
ex) 예제
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void timeover(int signum)
{
printf("\n\ntime over!!\n\n");
exit(0);
}
void main()
{
char buf[1024];
char *alpha = "abcdefghijklmnopqrstuvwxyz";
int timelimit;
struct sigaction act;
act.sa_handler = timeover;
sigaction(SIGALARM, &act, NULL);
printf("input timelimit (sec)..\n");
scanf("%d", &timelimit);
alarm(timelimit);
printf("START!!\n");
scanf("%s", buf);
if(!strcmp(buf, alpha))
printf("well done..you succeed!\n");
else
printf("sorry.. you fail!\n");
}
6)pause
-시그널이 전달될 때까지 대기한다.
*int pause(void)
-항상 -1을 반환한다.
"pause를 실행하면 프로세스는 임의의 시그널이 수신될 때까지 대기 상태가 된다.
ex)예제
#include <unistd.h>
#Include <signal.h>
void main()
{
printf("pause return %d\n", pause());
}
-----ctrl+c를 입력해서 대기 상태 끝내기
OS/리눅스 & 유닉스 2012. 1. 19. 16:02
내부 프로세스들 끼리의 통신을 위한 IPC 방법 중에 UDS 를 이용하여 프로그램을 작성하는 방법에 대해 알아 봅니다.
UDS
UDS(Unix Domain Socket)은 내부 프로세스들 끼리 TCP 또는 UDP 프로토콜을 이용하여 통신할 수 있도록 도와주는 소켓입니다. 이 소켓을 이용하시면 기존의 TCP/IP, UDP/IP에서 사용하던 함수로 같은 방법을 사용하여 프로세스들끼리 통신할 수 있어 매우 편리하며, 이점이 장점입니다.
말씀 드린 바와 같이 TCP/IP, UDP/IP와 같은 함수를 사용하고 다만 필요한 변수값만 바꾸어 주면 됩니다. 예를 들어 소켓을 생성해 보겠습니다.
TCP/IP와 UDP/IP에서 처럼 socket() 함수를 이용합니다. 우선 #include <sys/socket.h> 대신에 #include <sys/un.h>를 사용합니다.
자, socket() 함수를 호출해 보겠습니다.
int sock;
sock = socket( PF_FILE, SOCK_DGRAM, 0);
지금 UDP 프로토콜을 이용하기 위한 UDS 소켓 하나를 만들었습니다. 기존의 UDP/IP와는 달리 PF_INET 대신에 PF_FILE을 사용했습니다. UDS는 데이터를 전송하기 위해 파일을 이용하기 때문입니다. UDS의 사용은 이와 같습니다. 기존의 TCP/IP, UDP/IP 통신에서 사용하는 함수를 그대로 사용하면서 다만 필요한 정보를 몇 가지 바꾸어 주기만 하면 됩니다. socket()에 대한 더 자세한 말씀은 "Unix C Reference의 11장 7절 소켓 열고 닫기"를 참고하십시오.
이번에는 bind() 함수를 이용하여 소켓에 통신 케이블을 연결해 보겠습니다. bind() 함수를 이용하기 위해서는 주소정보가 필요합니다. 이를 위해서는 struct sockaddr_in 대신에 struct sockaddr_un을 사용합니다.
struct sockaddr_un server_addr;
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family = AF_UNIX;
strcpy( server_addr.sun_path, "/tmp/test_server.dat");
역시 TCP/IP, UDP/IP에서 사용했던 루틴이죠? 다만 sun_family 속성에 AF_UNIX 또는 AF_FILE로 대입해 주고, sun_path에 통신에 사용할 파일을 지정해 주시면 됩니다.
이후로는 기존의 TCP/IP, UDP/IP 프로그램하고 똑 같습니다!! 너무 멋지지 않습니까? 다만 소켓을 close() 해도 에 지정한 파일이 삭제되지 않아서 실행하기 전에 미리 삭제해 주어야 한다는 것 외에는 기존 루틴을 그대로 사용할 수 있습니다. 이런 귀찮은 것도 아래 코드로 간단히 해결할 수 있습니다.
if ( 0 == access( "/tmp/test_server.dat", F_OK))
unlink( "/tmp/test_server.dat");
이제 UDS를 이용한 TCP와 UDP 프로토콜 구현하는 프로그램을 보도록 하겠습니다.
UDS를 이용한 TCP
아래의 헤더파일을 꼭 포함합니다.
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/un.h>
서버 소켓에 사용할 파일의 이름을 상수로 정의하겠습니다. 이 파일은 서버 프로그램이나 클라이언트나 모두 같이 사용되는 파일 이름입니다.
#define FILE_SERVER "/tmp/test_server"
예제 프로그램에서는 클라이언트 프로그램이 서버 프로그램에게 문자를 전송하면 서버가 문자열의 길이와 전송한 문자열을 반송하는 프로그램을 작성해 보겠습니다.
TCP 서버 프로그램
소켓 변수를 선언하고 소켓을 생성합니다. PF_INET가 아닌 PF_FILE입니다.
int server_socket;
server_socket = socket( PF_FILE, SOCK_STREAM, 0);
이제 리슨 대기 모드에 사용할 서버 소켓으로 등록하기 위해 먼저 bind() 로 소켓에 주소 정보를 지정합니다. struct sockaddr_un 구조체 변수를 선언부터 하겠습니다.
struct sockaddr_un server_addr;
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family = AF_UNIX;
strcpy( server_addr.sun_path, FILE_SERVER); // 통신에 사용할 파일 이름을 복사해 줍니다.
if( -1 == bind( server_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
{
printf( "bind() 실행 에러n");
exit( 1);
}
bind() 실행에 에러가 없다면 이제 리슨 대기 모드로 진입합니다.
if ( -1 == listen(server_socket, 5))
{
printf( "대기상태 모드 설정 실패n");
exit( 1);
}
listen()에서 에러 없이 복귀했다면 클라이언트로부터 연결이 요청된 것입니다. accept() 로 접속을 수락합니다. 클라이언트 주소 정보를 위해 struct sockaddr_un 구조체 변수를 선언하겠습니다.
struct sockaddr_un client_addr;
int client_socket;
int client_addr_size;
client_addr_size = sizeof( client_addr);
client_socket = accept( server_socket, (struct sockaddr*)&client_addr, &client_addr_size);
if ( -1 == client_socket)
{
printf( "클라이언트 연결 수락 실패n");
exit( 1);
}
accept() 까지 해서 접속이 이루어 졌습니다. 이제 read()/write()를 이용하여 통신을 하면 되겠습니다. 클라이언트로부터 수신한 문자열의 길이를 구한 후 클라이언트로 전송합니다.
read ( client_socket, buff_rcv, BUFF_SIZE);
sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv);
write( client_socket, buff_snd, strlen( buff_snd)+1); // +1: NULL까지 포함해서 전송
그리고 close()를 이용하여 클라이언트와의 연결을 종료합니다.
close( client_socket);
TCP 클라이언트 프로그램
역시 소켓부터 생성합니다.
int client_socket;
client_socket = socket( PF_FILE, SOCK_STREAM, 0);
목적지인 서버 시스템의 주소 정보를 준비합니다.
struct sockaddr_un server_addr;
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family = AF_UNIX;
strcpy( server_addr.sun_path, FILE_SERVER); // 역시 서버 프로그램에서 사용하는 파일을 지정했습니다.
이제 서버와 접속을 시도합니다.
if( -1 == connect( client_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
{
printf( "접속 실패n");
exit( 1);
}
connect() 함수에서 에러 없이 복귀했다면 접속이 된 것입니다. 이제 read()/write() 를 이용하여 서버와 통신합니다. 프로그램을 실행할 때 인수로 받은 문자열을 전송합니다.
write( client_socket, argv[1], strlen( argv[1])+1); // +1: NULL까지 포함해서 전송
서버에서는 전송된 문자열에 대한 문자열 길이를 계산해서 다시 보내 줍니다. 서버가 보내준 데이터를 받아서 화면에 출력한 후 close() 함수로 연결을 종료합니다.
read ( client_socket, buff, BUFF_SIZE);
printf( "%sn", buff);
close( client_socket);
TCP 서버 프로그램 소스
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#define BUFF_SIZE 1024
#define FILE_SERVER "/tmp/test_server"
int main( void)
{
int server_socket;
int client_socket;
int client_addr_size;
int option;
struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
char buff_rcv[BUFF_SIZE+5];
char buff_snd[BUFF_SIZE+5];
if ( 0 == access( FILE_SERVER, F_OK))
unlink( FILE_SERVER);
server_socket = socket( PF_FILE, SOCK_STREAM, 0);
if( -1 == server_socket)
{
printf( "server socket 생성 실패n");
exit( 1);
}
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family = AF_UNIX;
strcpy( server_addr.sun_path, FILE_SERVER);
if( -1 == bind( server_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
{
printf( "bind() 실행 에러n");
exit( 1);
}
while( 1)
{
if( -1 == listen(server_socket, 5))
{
printf( "대기상태 모드 설정 실패n");
exit( 1);
}
client_addr_size = sizeof( client_addr);
client_socket = accept( server_socket, (struct sockaddr*)&client_addr, &client_addr_size);
if ( -1 == client_socket)
{
printf( "클라이언트 연결 수락 실패n");
exit( 1);
}
read ( client_socket, buff_rcv, BUFF_SIZE);
printf( "receive: %sn", buff_rcv);
sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv);
write( client_socket, buff_snd, strlen( buff_snd)+1); // +1: NULL까지 포함해서 전송
close( client_socket);
}
return 0;
}
TCP 클라이언트 프로그램 소스
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#include "sample.h"
#define BUFF_SIZE 1024
#define FILE_SERVER "/tmp/test_server"
int main( int argc, char **argv)
{
int client_socket;
struct sockaddr_un server_addr;
char buff[BUFF_SIZE+5];
client_socket = socket( PF_FILE, SOCK_STREAM, 0);
if( -1 == client_socket)
{
printf( "socket 생성 실패n");
exit( 1);
}
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sun_family = AF_UNIX;
strcpy( server_addr.sun_path, FILE_SERVER);
if( -1 == connect( client_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
{
printf( "접속 실패n");
exit( 1);
}
write( client_socket, argv[1], strlen( argv[1])+1); // +1: NULL까지 포함해서 전송
read ( client_socket, buff, BUFF_SIZE);
printf( "%sn", buff);
close( client_socket);
return 0;
}
UDS를 이용한 UDP 프로그램
앞서 UDS를 이용한 TCP 프로그램을 이해하셨다면 UDP는 별반 다를 것이 없습니다. 예제에서는 두 개의 프로그램을 작성하며, 한쪽에서 문자열을 전송하면 받는 쪽은 문자열을 겹쳐서 반송해 줍니다. 이렇게 자료를 주고 받을 수 있도록 하기 위해 각각의 프로그램에 파일을 따로 따로 두도록 하겠습니다.
예제 프로그램 1은 /tmp/process_a 를 프로그램 2는 /tmp/process_b 파일을 사용하도록 하겠습니다.
UDS를 이용한 UDP 프로그램 1
프로그램 1에서 사용할 소켓용 파일은 /tmp/process_a 입니다.
#define SOCK_LOCALFILE "/tmp/process_a"
이전 실행에서 생성한 소켓용 파일을 삭제하겠습니다.
if ( 0 == access( SOCK_LOCALFILE, F_OK))
unlink( SOCK_LOCALFILE);
역시 소켓부터 만들겠습니다.
int sock;
sock = socket( PF_FILE, SOCK_DGRAM, 0);
상대로부터의 자료를 수신하기 위해 주소정보를 준비하고 bind()함수로 소켓에 주소정보를 할당하고 커널에 올립니다.
struct sockaddr_un local_addr;
memset( &local_addr, 0, sizeof( local_addr));
local_addr.sun_family = AF_UNIX;
strcpy( local_addr.sun_path, SOCK_LOCALFILE);
if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr)) )
에러없이 bind()가 실행되었다면 이제 recvfrom() 함수와 sendto() 함수를 이용하여 통신할 수 있습니다. recvfrom() 함수를 이용하여 상대로부터 전송되어온 전문을 수신합니다.
addr_size = sizeof( guest_addr);
recvfrom( sock, buff_rcv, BUFF_SIZE, 0 , ( struct sockaddr*)&guest_addr, &addr_size);
buff_rcv에는 수신한 데이터가 guest_addr 에는 전송지의 주소 정보가 있습니다. 예제인만큼 주소지 정보를 출력해 보겠습니다. 출력해 보면 두번째 프로그램의 소켓 파일인 /tmp/process_b 가 출력될 것입니다.
printf( "%sn", guest_addr.sun_path); // 게스트 프로세스의 소켓용 파일 이름을 출력해 봅니다.
이제 수신한 자료를 중복하여 겹친 후 전송지로 다시 보내 줍니다.
sprintf( buff_snd, "%s%s", buff_rcv, buff_rcv);
sendto( sock, buff_snd, strlen( buff_snd)+1, 0,
( struct sockaddr*)&guest_addr, sizeof( guest_addr)); // +1: NULL까지 포함해서 전송
UDS를 이용한 UDP 프로그램 2
프로그램 2은 실행 시 입력받은 인자를 전송하고 다시 프로그램 1에서 전송한 데이터를 출력해 보겠습니다. 우선 소켓용 파일을 정리합니다. 이번에는 SOCK_LOCALFILE 은 /tmp/process_b 임을 확인하여 주십시오.
#define SOCK_LOCALFILE "/tmp/process_b"
#define SOCK_TARGETFILE "/tmp/process_a"
이전 실행으로 생성된 소켓용 파일을 삭제합니다.
if ( 0 == access( SOCK_LOCALFILE, F_OK))
unlink( SOCK_LOCALFILE);
자, 소켓을 만듭니다.
int sock;
sock = socket( PF_FILE, SOCK_DGRAM, 0);
상대편이 제대로된 주소 정보를 받을 수 있고, 또한 자신도 다른 프로세스로부터 자료를 수신 받기 위해 소켓에 주소정보를 할당해 줍니다.
struct sockaddr_un local_addr;
memset( &local_addr, 0, sizeof( local_addr));
local_addr.sun_family = AF_UNIX;
strcpy( local_addr.sun_path, SOCK_LOCALFILE);
if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr)))
이제 데이터를 전송할 목적지 주소를 구성합니다.
struct sockaddr_un target_addr;
memset( &target_addr, 0, sizeof( target_addr));
target_addr.sun_family = AF_UNIX;
strcpy( target_addr.sun_path, SOCK_TARGETFILE);
목적지인 프로그램 1로 자료를 전송합니다.
sendto( sock, argv[1], strlen( argv[1])+1, 0,
( struct sockaddr*)&target_addr, sizeof( target_addr)); // +1: NULL까지 포함해서 전송
다시 프로그램 1로부터 recvfrom()을 이용하여 자료를 수신합니다. 이때 프로그램 1 에서 처럼 recvfrom()에서 송신지의 주소 정보를 구할 수 있으나, 이미 알고 있으므로 인수에 NULL로 간편화 하겠습니다.
recvfrom( sock, buff_rcv, BUFF_SIZE, 0 , NULL, 0);
수신한 자료를 출력하고 close()로 소켓을 닫아 통신을 종료하겠습니다.
printf( "%sn", buff_rcv);
close( sock);
UDP 프로그램 A
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#define BUFF_SIZE 1024
#define SOCK_LOCALFILE "/tmp/process_a"
int main( void)
{
int sock;
size_t addr_size;
struct sockaddr_un local_addr;
struct sockaddr_un guest_addr;
char buff_rcv[BUFF_SIZE+5];
char buff_snd[BUFF_SIZE+5];
if ( 0 == access( SOCK_LOCALFILE, F_OK))
unlink( SOCK_LOCALFILE);
sock = socket( PF_FILE, SOCK_DGRAM, 0);
if( -1 == sock)
{
printf( "socket 생성 실패n");
exit( 1);
}
memset( &local_addr, 0, sizeof( local_addr));
local_addr.sun_family = AF_UNIX;
strcpy( local_addr.sun_path, SOCK_LOCALFILE);
if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr)) )
{
printf( "bind() 실행 에러n");
exit( 1);
}
while( 1)
{
addr_size = sizeof( guest_addr);
recvfrom( sock, buff_rcv, BUFF_SIZE, 0 ,
( struct sockaddr*)&guest_addr, &addr_size);
printf( "receive: %sn", buff_rcv);
printf( "%sn", guest_addr.sun_path); // 게스트 프로세스의 소켓용 파일 이름을 출력해 봅니다.
sprintf( buff_snd, "%s%s", buff_rcv, buff_rcv);
printf( "%sn", buff_snd);
sendto( sock, buff_snd, strlen( buff_snd)+1, 0,
( struct sockaddr*)&guest_addr, sizeof( guest_addr));// +1: NULL까지 포함해서 전송
}
}
UDP 프로그램 B
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#define BUFF_SIZE 1024
#define SOCK_LOCALFILE "/tmp/process_b"
#define SOCK_TARGETFILE "/tmp/process_a"
int main( int argc, char **argv)
{
int sock;
struct sockaddr_un local_addr;
struct sockaddr_un target_addr;
char buff_rcv[BUFF_SIZE+5];
if ( 0 == access( SOCK_LOCALFILE, F_OK))
unlink( SOCK_LOCALFILE);
sock = socket( PF_FILE, SOCK_DGRAM, 0);
if( -1 == sock)
{
printf( "socket 생성 실패n");
exit( 1);
}
memset( &local_addr, 0, sizeof( local_addr));
local_addr.sun_family = AF_UNIX;
strcpy( local_addr.sun_path, SOCK_LOCALFILE);
if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr)))
{
printf( "bind() 실행 에러n");
exit( 1);
}
memset( &target_addr, 0, sizeof( target_addr));
target_addr.sun_family = AF_UNIX;
strcpy( target_addr.sun_path, SOCK_TARGETFILE);
sendto( sock, argv[1], strlen( argv[1])+1, 0,
( struct sockaddr*)&target_addr, sizeof( target_addr)); // +1: NULL까지 포함해서 전송
recvfrom( sock, buff_rcv, BUFF_SIZE, 0 , NULL, 0);
printf( "%sn", buff_rcv);
close( sock);
return 0;
}
OS/리눅스 & 유닉스 2012. 1. 19. 14:58
1. 정의 형식
스크립트 규칙을 가지고 정의된 파일입니다. 스크립트는 한 줄에 하나의 명령을 정의할 수 있으며 각 줄에 쓰여진 명령들은 무조건 지정되는 것이 아니고 아래의 형식을 가지고 있습니다.
id : runlevels : action : process
id
| inittab 파일 안에서 구성되는 단위 식별자
|
runlevels
|
특정한 프로세스들만 실행하기 위한 0부터 6까지의 레벨로써 선택 상항이 필요한 프로세스들을 실행하게 만든다. 같은 번호를 가진 복수개의 런레벨을 명시함으로써 여러 프로세스들의 묶음을 한번에 실행하게 할 수 있다.
|
process
| 현재 실행될 프로세스 파일의 경로와 이름을 명시한다.
|
action
| 특정한 지시어로 프로세스를 실행할 수 있게 만든다.
|
respawn
| 프로세스가 중지되면 언제든지 재작동한다. 따라서, 사용자가 login을 할 수있게 하는 getty같은 것은 respawn 으로 되어있어야 한다. 그러면, logout한 후에 언제든지 다시 getty가 동작하여 새롭게 login할 수있게 한다.
|
wait
| 프로세스가 특정동작 수준에서 한번 시작되면, init가 그것이 끝날 때까지 기다린다. 다음줄로 넘어가지 않는다.
|
once
| 실행되지 않은 프로세스일 경우에 특정 동작 수준에서 한번만 실행된다. 단 wait처럼 기다리지 않는다.
|
boot
| 시스템이 부팅될 때 실행되는 프로세스를 가리킨다. 런레벨은 무시된다.
|
bootwait
| 프로세스는 시스템이 부팅되는 동안 실행되며, 프로세스가 끝날 때까지 기다린다.
|
off
| 사용되지 않는다. 아무것도 하지 말라는 뜻.
|
ondemand
| ondemand runlevel로 마크된 프로세스는 특정한 ondermand runlevel이 호줄될 때마다 다시 실행하게 된다.
|
initdefault
| initdefault 엔트리는 시스템이 부팅된 후에 어떤 runlevel로 들어 갈 것인가를 정한다. 만일 존재하지 않는다면, init는 콘솔상에서 runlevel를 물어 볼것이다. process 필드는 아무런 의미가 없게 된다.
|
sysinit
| 프로세스가 시스템에 부팅될 때 어떤 boot나 bootwait로 지정된프로세스보다 먼저 실행된다. 런레벨 필드는 무시한다.
|
powerwait
| init가 SIGPWR 신호를 받게 되면 이 프로세스가 실행하며, 전원에 이상이 있음을 알린다. init는 프로세스가 종료될 때 까지 기다린다.
|
powerfail
| powerwait 와 동일하며, 단지 init가 프로세스를 기다리지 않는다.
|
powerokwait
| powerwait 와 동일하나 /etc/powerstatus 파일에 OK라는 단어가 있을 때만 실행된다. 즉 전원이 돌아왔을 때 실행된다. /etc/powerstatus 파일은 직접 만들어 줘야 한다.
|
ctrlaltdel
| init가 SIGNT 신호를 받으며 이 프로세스가 실행된다. 따라서, 시스템콘솔상에 누군가 ctrl+alt+del 키를 눌렀음을 의미한다. 전형적으로, 누군가 단일 사용자 모드나 재부팅할 때 shutdown이 실행된다.
|
kbrequest
| init가 키보드 핸들러부터 콘솔에서 특수키 조합이 눌러졌다는 시그널을 받으면 프로세스가 실행된다. 키맵 파일과 함께 쓰일 수 있다.
|
OS/리눅스 & 유닉스 2012. 1. 12. 15:01
shmctl() 함수는 공유 메모리에 대한 정보를 구하거나 변경 또는 제거합니다. 공유 메모리에 대한 자세한 내용은 shmget() 함수의 내용을 참고하여 주십시오.
헤더 |
#include <sys/ipc.h>
#include <sys/shm.h>
|
형태 |
int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
인수 |
int shmid |
공유 메모리 식별 번호 |
int cmd |
제어 명령 |
struct shmid_ds *buf |
공유 메모리 정보 구하기 위한 버퍼 포인터
struct shmid_ds {
struct ipc_perm shm_perm;/* 접근권한 */
int shm_segsz; /* 세그먼트의 크기(bytes) */
time_t shm_atime; /* 마지막 접근 시간 */
time_t shm_dtime; /* 마지막 제거 시간 */
time_t shm_ctime; /* 마지막 변경 시간 */
unsigned short shm_cpid; /* 생성자의 프로세스의 프로세스 id */
unsigned short shm_lpid; /* 마지막으로 작동한 프로세스의 프로세스 pid */
short shm_nattch; /* 현재 접근한 프로세스의 수 */
/* 다음은 개별적이다 */
unsigned short shm_npages; /* 세그먼트의 크기(pages) */
unsigned long *shm_pages;
struct shm_desc *attaches; /* 접근을 위한 기술자들 */
};
shm_perm 멤버의 필드들은 아래와 같이 설정할 수 있습니다.
struct ipc_perm{
key_t key;
ushort uid; /* owner의 euid 와 egid */
ushort gid;
ushort cuid; /* 생성자의 euid 와 egid */
ushort cgid;
ushort mode; /* 접근 모드의 하위 9 bits */
ushort seq; /* 연속 수(sequence number) */
};
|
|
반환 |
|
예제
예제를 위해 두 개의 프로세스를 만들겠습니다. counter.c 는 공유 메모리에 1초 마다 0부터 계속 증가하는 카운터 문자열을 공유 메모리에 넣으면 show_counter.c에서 공유 메모리를 화면에 출력하도록 하겠습니다.
#include <stdio.h> // printf()
#include <unistd.h> // sleep()
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#define KEY_NUM 9527
#define MEM_SIZE 1024
int main( void)
{
int shm_id;
void *shm_addr;
struct shmid_ds shm_info;
if ( -1 == ( shm_id = shmget( (key_t)KEY_NUM, MEM_SIZE, IPC_CREAT¦0666)))
{
printf( "공유 메모리 생성 실패\n");
return -1;
}
else
{
printf( "공유 메모리 생성 성공\n");
}
if ( ( void *)-1 == ( shm_addr = shmat( shm_id, ( void *)0, 0)))
{
printf( "공유 메모리 첨부 실패\n");
return -1;
}
else
{
printf( "공유 메모리 첨부 성공\n");
}
if ( -1 == shmctl( shm_id, IPC_STAT, &shm_info))
{
printf( "공유 메모리 정보 구하기에 실패했습니다.\n");
return -1;
}
printf( "공유 메모리를 사용하는 프로세스의 개수 : %d\n", shm_info.shm_nattch);
if ( -1 == shmdt( shm_addr))
{
printf( "공유 메모리 분리 실패\n");
return -1;
}
else
{
printf( "공유 메모리 분리 성공\n");
}
if ( -1 == shmctl( shm_id, IPC_RMID, 0))
{
printf( "공유 메모리 제거 실패\n");
return -1;
}
else
{
printf( "공유 메모리 제거 성공\n");
}
return 0;
}
]$ gcc main.c
]$ ./a.out
공유 메모리 생성 성공
공유 메모리 첨부 성공
공유 메모리를 사용하는 프로세스의 개수 : 1
공유 메모리 분리 성공
공유 메모리 제거 성공
]$
OS/리눅스 & 유닉스 2012. 1. 12. 10:30
■ 명령어 정리
vi 시작 명령어
|
vi 파일명
| 파일 열기, 작성
|
vi +18 파일명
| 18행으로 파일 열기
|
vi +/"문자열" fn
| "문자열"의 처음발생 단어부터 열기
|
vi -r 파일명
| 손상된 파일 회복
|
view 파일명
| 읽기 전용으로 파일 열기
|
vi 커서 이동 명령어
|
h(←)
| 좌측으로
|
l(→)
| 우측으로
|
j(↓)
| 아래로
|
k(↑)
| 위로
|
w
| 오른쪽 한 단어의 끝 부분으로, word
|
e
| 오른쪽 한 단어의 앞 부분으로
|
b
| 왼쪽 한 단어의 앞 부분으로, back
|
Enter
| 한 행 아래로
|
Back space
| 한 문자 왼쪽으로
|
Space Bar
| 한 문자 오른쪽으로
|
^
| 행의 맨 왼쪽으로
|
$
| 행의 맨 오른쪽으로
|
H
| 화면의 맨 위로, Home
|
M
| 화면의 중간으로, Middle
|
L
| 화면의 맨 아래로, Last
|
숫자G
| '숫자'만큼 지정한 줄로
|
Ctrl + f
| 한 화면 위로
|
Ctrl + b
| 한 화면 아래로, bottom
|
Ctrl + d
| 반 화면 위로, down
|
Ctrl + u
| 반 화면 아래로, up
|
Ctrl + e
| 한 줄씩 위로
|
Ctrl + y
| 한 줄씩 아래로
|
문자,행 삽입 명령어
|
a (종료:ESC)
| 커서 오른쪽에 문자 삽입 , append
|
A (종료:ESC)
| 커서 오른쪽, 행의 끝에 문자 삽입 , Append
|
i (종료:ESC)
| 커서 왼쪽에 문자 삽입, insert
|
I (종료:ESC)
| 커서 왼쪽, 행의 처음에 문자 삽입
|
o (종료:ESC)
| 커서 아래에 행 삽입,
|
O (종료:ESC)
| 커서 위에 행 삽입
|
텍스트 변경 명령어
|
cw (종료:ESC)
| 단어 변경
|
cc (종료:ESC)
| 행 변경
|
C (종료:ESC)
| 커서 오른쪽의 행 변경
|
s (종료:ESC)
| 커서가 위치한 문자열 대체
|
S (종료:ESC)
| 커서가 위치한 라인의 문자열 대체
|
r
| 커서 위치 문자를 다른 문자로 대체 , replace
|
r-Enter
| 행 분리
|
J
| 현재 행과 아래 행 결합
|
xp
| 커서 위치 문자와 오른쪽 문자 교환
|
~
| 문자형(대,소문자) 변경
|
u
| 이전 명령 취소 , undo
|
U
| 행 변경 사항 취소
|
:u
| 이전의 최종 행 취소
|
.
| 이전 최종 명령 반복
|
텍스트 삭제 명령어
|
x
| 커서가 있는 문자 삭제
|
5x
| 커서가 있는 위치부터 5개의 문자를 삭제
|
dw
| 현재 커서가 있는 한단어 삭제
|
dd
| 커서가 있는 라인 삭제
|
5dd
| 커서가 있는 라인부터 5개의 라인 삭제
|
db
| 커서의 위치에서 꺼꾸로 한단어 삭제
|
D
| 커서 오른쪽 행 삭제
|
:5,10d
| 5-10번째 행 삭제
|
복사 및 이동 명령어
|
yy
| 행 yank 또는 복사
|
Y
| 행 yank 또는 복사
|
P
| yank되거나 삭제된 행 현재 행 위에 삽입 , Paste
|
p
| yank되거나 삭제된 행 현재 행 아래에 삽입 ,paste
|
:1,2 co 3
| 1-2행을 3행 다음으로 복사, copy
|
:4,5 m 6
| 4-5행을 6행 위로 이동, move
|
행 번호 설정 명령어
|
:set nu
| 행 번호 표시 , number
|
:set nonu
| 행 번호 숨기기 , nonumber
|
행 찾기 명령어
|
G
| 파일의 마지막 행으로 가기
|
21G
| 파일의 21번째 행으로 가기
|
Ctrl + G
| 현재의 filename과 line수를 알려줌
|
탐색 및 대체 명령어
|
/검색할 문자열/
| 오른쪽 아래 방향으로 문자열 검색
|
?검색할 문자열?
| 왼쪽 위 방향으로 문자열 검색
|
n
| 문자열의 다음으로 계속 검색
|
N
| 문자열의 이전으로 계속 검색
|
:g/search-string/s//replace-string/gc
| 각 발생 탐색 후 확인하고 대체
|
:s/str/rep/
| 현재 행의 str을 rep로 대체
|
:1,.s/str/rep/
| 1부터 현재 행의 str을 rep로 대체
|
:%s/str/rep/g
| 파일 전체 str을 rep로 전부 대체
|
:.$/aaa/bbb/
| 커서의 위치로부터 화일의 끝까지 있는 모든 aaa를bbb로 대체
|
화면정리 명령어
|
Ctrl + l
| 불필요한 화면정리 후 다시 표시
|
파일 명령어
|
:r filename
| 커서 다음에 파일 삽입
|
:34 r filename
| 파일을 34번째 행 다음에 삽입
|
보관 및 종료 명령어
|
:w
| 변경사항 보관
|
:w filename
| 버퍼를 파일로 보관
|
:wq
| 변경사항 보관 후 vi 종료
|
ZZ
| 변경사항 보관 후 vi 종료
|
:q!
| 변경사항 보관하지 않고 종료
|
q
| 수정한 파일을 저장하지 않고 vi 종료
|
e!
| 수정한 것을 무시하고 다시 편집상태로
|
OS/리눅스 & 유닉스 2012. 1. 11. 17:46
== vi : Visual의 약자
== 시작 : vi [파일명] 으로 호출. (일반적인 방법) (파일이 현재 폴더에 O) 해당 파일을 수정. (파일이 현재 폴더에 X) 해당 파일명으로 파일 생성.
== 종료 : ZZ / :q / :wq ZZ - 임시 버퍼의 내용을 vi로 호출할때 사용되었던 파일에 기록한 후 vi를 빠져나옴. :q - 버퍼의 내용을 기록하지 않고 vi 종료. :wq - ZZ 명령과 같음. :w(기록)과 :q(종료) 를 연속적으로 수행.
== vi 사용법
(1) 화면제어 ctrl + f 화면 전체의 내용이 한 화면 위로 이동 [화면] ctrl + b 화면 전체의 내용이 한 화면 아래로 이동 [화면] ctrl + e 화면 전체의 내용이 한 줄씩 위로 이동 [줄] ctrl + y 화면 전체의 내용이 한 줄씩 아래로 이동 [줄]
(2) 커서제어 enter key 다음행의 처음으로 이동 j 한행 아래로 이동 k 한행 위로 이동 l 현재행의 오른쪽으로 이동 h 현재행의 왼쪽으로 이동
w 다음단어 첫자로 이동 b 바로 이전 단어의 앞 빈칸으로 이동 e 현재 단어의 끝자로 이동
^ 행의 제일 왼쪽으로 이동 $ 행의 제일 오른쪽으로 이동 H 화면의 좌측 상단으로 이동 M 화면의 좌측 중간으로 이동 L 화면의 좌측 하단으로 이동 숫자G 지정한 숫자의 행으로 이동 :숫자 /문자열/ 오른쪽 아래 방향으로 문자열 검색 ?문자열? 왼쪽 아래 방향으로 문자열 검색 n 아래로 검색을 계속함 N 위로 검색을 계속함 ctrl + g 현재의 파일이름과 행 수를 알려줌
(3) 문안편집 a (append) 현재 커서가 위치한 문자의 다음위치부터 입력 A 현재 커서가 있는 행의 마지막부터 입력 i (insert) 현재 커서가 있는 위치부터 입력 I 현재 커서가 있는 행의 처음부터 입력 o (open) 현재 커서가 있는 행의 다음행에 새로운 한행을 삽입하며 입력 O 현재 커서가 있는 위치에 새로운 한행을 삽입하며 입력
(4) 문안삭제 x 커서위치의 한문자 삭제 dw 커서위치의 한행을 삭제 dd 커서위치의 한행을 삭제 :1,$ d 1행부터 마지막행까지 삭제 D 커서위치의 오른쪽 모두 삭제
(5) 문안의 이동 및 복사 nyy n행을 버퍼에 복사 p 버퍼에 복사된 내용을 화면에 인쇄
(6) 기타 명령어 U (Undo) 직전에 수행한 명령을 취소 또는 복구 r 한문자 변경 cw 한단어 변경 J 행 결합 enter key 행 삽입 :1,$ s/test/TEST/g 문장의 1행부터 마지막행까지 "test"를 "TEST"로 바꾼다 :sh shell 수행 :![unix명령어] unix명령어 를 수행하고 vi로 돌아온다 :r [파일이름] 지정한 파일을 실행하고 vi로 돌아온다 :w [파일이름] 현재 내용을 지정한 파일명으로 저장 :1, 10 w [파일명] 1행부터 10행까지만 지정한 파일이름으로 저장
OS/리눅스 & 유닉스 2012. 1. 9. 16:44
:1,$s/원본문자열/치환할문자열/g
1,$ --> 줄첫번째부터 마지막까지
s --> string을..
/원본문자열/치환할문자열/ --> 원본문자열을 치환할문자열로
g --> 파일 전체(global)에 대해서
OS/리눅스 & 유닉스 2012. 1. 5. 08:37
http://blog.naver.com/illi0001?Redirect=Log&logNo=140110607926
sed 편집기
한 번에 한 줄씩 파일(또는 입력 내용)을 처리해서 결과를 화면에 보낸다.
1. 정규 표현식
sed는 정규표현식과 메타 문자를 이용햇 파일에서 패턴을 검색하는 것이 grep과유사하고,
정규 표현은 검색과 치환을 위해 슬래시(/)로 글자는 묶는 패턴이다.
- sed -n '/RE/p' filename
- sed -n '/love/p' filename
- sed -n 's/RE/replacement string/' filename
- sed -n 's/love/like/' filename
정규 표현식 구분자를 변경하려면, 역슬래시를 이용
문자 c를 정규표현식 구분자로 사용하려면, c 앞에 역슬래시를 오게 한 후 패턴을 지정하고 이어 구분자 c로 끝내면 된다.
- sed -n '/love/p' filename
- sed -n '\cREcp' filename
문자 x로 역슬래시를 대신
2. 주소 지정
주소를 지정해서 편집하고자 하는 줄을 선택
sed 명령은 sed가 해당 줄에 대해서 출력, 삭제, 변경 등과 같이 어떤 작업을 해야 하는지 알려준다.
- sed '1, 3d' myfile
. myfile 의 1~3 줄을 삭제
- sed -n '/[jJ]ohn/p' datafile
. datafile에서 john, John 패턴과 일치하는 줄만 출력
3. 명령과 옵션
sed 명령은 주소로 지정된 각 줄을 처리하고, 지정하지 않으면, 입력 내용의 모든 줄을 처리
- sed 명령
- sed 옵션
- 다중 명령이 사용되거나 주소를 주소 범위 내에 중첩할 필요가 있을 때는, 명령을 중괄호로 묶고 각 명령들을 다른 줄에 입력하거나,
명령 끝에 세미콜론(;)을 붙여야 한다. 느낌표(!)는 명령을 거꾸로 해설할 때 사용.
. sed '/Tom/d' file
- Tom이 포함된 모든 줄을 삭제
. sed '/Tom/!d' file(sh, ksh, bash)
. sed '/Tom/\!d' file(csh, tcsh)
- Tom이 포함되지 않은 줄을 삭제
- sed는 비파괴적인 편집기로 사용자가 편집한 내용을 사용자 하면에 출력하며, 상요자가 편집한 것에 대해 파일을 변경하지 않음
파일에 편집하 것을 변경하기 위해서는, 결과를 다른 파일로 리다이렉트 해야 하고, 원래 파일은 이름을 변경
. sed '1, 3d' filex > temp
. mv temp filex
4. sed의 정규표현 메타문자
5. sed 예제
- p 명령 : 출력
. sed '/north/p' datafile
- 기본적으로 모든 줄을 출력하고, 패턴과 일치하는 줄을 한번 더 출력
. sen -n '/north/p' datafile
- 기본 출력 기능을 금지하고, 패턴과 일치하는 줄만 출력
- d 명령 : 삭제
. sed '3d' datafile
- 3번째 줄을 삭제하고, 나머지 모든 줄을 출력
. sed '3, $d' datafile
- 3번째 줄부터 마지막 줄까지 삭제하고, 나머지 모든 줄을 출력
. sed '$d' datafile
- 마지막 줄을 삭제하고, 나머지 모든 줄을 출력
. sed '/north/d' datafile
- 패턴과 일치하는 줄을 삭제하고, 나머지 줄을 출력
- s 명령 : 치환
. g 플래그는 전역적인 치환
. sed 's/west/north/g' datafile
- west를 north로 치환
. sed -n 's/^west/north/p' datafile
- west로 시작하는 줄을 north로 치환하고, 그 줄만 출력
. sed -n 's/west/east/gp' datafile
- 콤마(,) : 범위 지정
. sed -n '/west/, /east/p' datafile
- west, east 사이의 패턴 범위에 있는 모든 줄을 출력
. sed -n '5,/^northeast/p' datafile
- 5번째 줄부터 northeast로 시작하는 첫 번째 줄까지 출력
- e 명령 : 다중 편집
. sed -e '1,3d' ?e 's/Hemenway/Jones/' datafile
- 1~3줄까지 삭제, Hemenway를 Jones로 치환
- r 명령 : 파일에서 읽기
. sed '/Suan/r newfile' datafile
- newfile 내용을 Suan과 일치하는 줄 다음으로 입력 데이터 파일로 읽힌다.
- w 명령 : 파일에 쓰기
. sed -n '/north/w newfile' datafile
- north 패턴을 포함하는 모든 줄을 newfile에 쓴다.
- a 명령 : 추가
. sed '/^north /a\ ###Sample Text###' datafile
- north로 시작하고 여백으로 이어지는 패턴과 일치하는 다음 줄 다음에 추가
- i 명령 : 삽입
. a 명령과 비슷하나 줄 위에 삽입
. sed '/eastern/i\ ###Sample Text###' datafile
- eastern 패턴과 일치하는 줄 위에 텍스트 삽입
- c 명령 : 변경
. sed '/eastern/c\ ###Sample Text###' datafile
- eastern 패턴과 일치하는 줄을 텍스트로 변환
- n 명령 : 다음
. sed '/eastern/{ n; s/AM/Archie/; }' datafile
- eastern 패턴과 일치하는 다음줄에서 AM을 Archie로 변환
- y 명령 : 변환
. sed '1,3y/abc...xyz/ABC...XYZ/' datafile
- 1~3줄의 소문자를 대문자로 변환
- q 명령 : 종료
. sed '5q' datafile
- 5번째 줄이 출력된 후 sed 종료
. sed '/Lewis/{ s/Lewis/Joseph/;q; }' datafile
- Lewis 패턴과 일치하면 Lewis를 Joseph으로 변경하고, sed 종료
- h, g 명령 : 보류와 얻기
. sed -e '/northeast/h' -e '$G' datafile
- northeast 패턴과 일치하는 줄을 마지막에 복사, 추가
. sed -e '/WE/{h; d;}' -e '/CT/{G;}' datafile
- WE를 포함하는 줄은 CT를 포함하는 줄 다음으로 이동
- h, x 명령 : 보류와 교체
. sed -e '/Patricia/h' -e '/Margot/x' datafile
- Margot을 포함하는 줄을 Patricia를 포함하는 줄로 교체
6. sed 리뷰
|